--- loncom/interface/loncommon.pm	2020/10/01 10:16:33	1.1348
+++ loncom/interface/loncommon.pm	2021/09/21 22:54:26	1.1366
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.1348 2020/10/01 10:16:33 raeburn Exp $
+# $Id: loncommon.pm,v 1.1366 2021/09/21 22:54:26 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -4893,6 +4893,59 @@ sub get_student_view_with_retries {
     }
 }
 
+sub css_links {
+    my ($currsymb,$level) = @_;
+    my ($links,@symbs,%cssrefs,%httpref);
+    if ($level eq 'map') {
+        my $navmap = Apache::lonnavmaps::navmap->new();
+        if (ref($navmap)) {
+            my ($map,undef,$url)=&Apache::lonnet::decode_symb($currsymb);
+            my @resources = $navmap->retrieveResources($map,sub { $_[0]->is_problem() },0,0);
+            foreach my $res (@resources) {
+                if (ref($res) && $res->symb()) {
+                    push(@symbs,$res->symb());
+                }
+            }
+        }
+    } else {
+        @symbs = ($currsymb);
+    }
+    foreach my $symb (@symbs) {
+        my $css_href = &Apache::lonnet::EXT('resource.0.cssfile',$symb);
+        if ($css_href =~ /\S/) {
+            unless ($css_href =~ m{https?://}) {
+                my $url = (&Apache::lonnet::decode_symb($symb))[-1];
+                my $proburl =  &Apache::lonnet::clutter($url);
+                my ($probdir) = ($proburl =~ m{(.+)/[^/]+$});
+                unless ($css_href =~ m{^/}) {
+                    $css_href = &Apache::lonnet::hreflocation($probdir,$css_href);
+                }
+                if ($css_href =~ m{^/(res|uploaded)/}) {
+                    unless (($httpref{'httpref.'.$css_href}) ||
+                            (&Apache::lonnet::is_on_map($css_href))) {
+                        my $thisurl = $proburl;
+                        if ($env{'httpref.'.$proburl}) {
+                            $thisurl = $env{'httpref.'.$proburl};
+                        }
+                        $httpref{'httpref.'.$css_href} = $thisurl;
+                    }
+                }
+            }
+            $cssrefs{$css_href} = 1;
+        }
+    }
+    if (keys(%httpref)) {
+        &Apache::lonnet::appenv(\%httpref);
+    }
+    if (keys(%cssrefs)) {
+        foreach my $css_href (keys(%cssrefs)) {
+            next unless ($css_href =~ m{^(/res/|/uploaded/|https?://)});
+            $links .= '<link rel="stylesheet" type="text/css" href="'.$css_href.'" />'."\n";
+        }
+    }
+    return $links;
+}
+
 =pod
 
 =item * &get_student_answers() 
@@ -5466,8 +5519,8 @@ sub blocking_status {
 
 # build a link to a popup window containing the details
     my $querystring  = "?activity=$activity";
-# $uname and $udom decide whose portfolio the user is trying to look at
-    if (($activity eq 'port') || ($activity eq 'passwd')) {
+# $uname and $udom decide whose portfolio (or information page) the user is trying to look at
+    if (($activity eq 'port') || ($activity eq 'about') || ($activity eq 'passwd')) {
         $querystring .= "&amp;udom=$udom"      if ($udom =~ /^$match_domain$/); 
         $querystring .= "&amp;uname=$uname"    if ($uname =~ /^$match_username$/);
     } elsif ($activity eq 'docs') {
@@ -5509,6 +5562,8 @@ END_MYBLOCK
         $text = &mt('Checking Critical Messages Blocked');
     } elsif ($activity eq 'reinit') {
         $text = &mt('Checking Course Update Blocked');
+    } elsif ($activity eq 'about') {
+        $text = &mt('Access to User Information Pages Blocked');
     }
     $output .= <<"END_BLOCK";
 <div class='$class'>
@@ -5537,7 +5592,8 @@ sub check_ip_acc {
         ($ENV{'REMOTE_ADDR'} eq &Apache::lonnet::get_host_ip($Apache::lonnet::perlvar{'lonHostID'}))) {
         $ip = $env{'request.host'} || $ENV{'REMOTE_ADDR'} || $clientip;
     } else {
-        $ip = $ENV{'REMOTE_ADDR'} || $env{'request.host'} || $clientip;
+        my $remote_ip = &Apache::lonnet::get_requestor_ip();
+        $ip = $remote_ip || $env{'request.host'} || $clientip;
     }
 
     my $name;
@@ -5689,6 +5745,17 @@ sub get_domainconf {
                                     }
                                 }
                             }
+                        } elsif ($key eq 'saml') {
+                            if (ref($domconfig{'login'}{$key}) eq 'HASH') {
+                                foreach my $host (keys(%{$domconfig{'login'}{$key}})) {
+                                    if (ref($domconfig{'login'}{$key}{$host}) eq 'HASH') {
+                                        $designhash{$udom.'.login.'.$key.'_'.$host} = 1;
+                                        foreach my $item ('text','img','alt','url','title','notsso') {
+                                            $designhash{$udom.'.login.'.$key.'_'.$item.'_'.$host} = $domconfig{'login'}{$key}{$host}{$item};
+                                        }
+                                    }
+                                }
+                            }
                         } else {
                             foreach my $img (keys(%{$domconfig{'login'}{$key}})) {
                                 $designhash{$udom.'.login.'.$key.'_'.$img} = 
@@ -6059,7 +6126,8 @@ other decorations will be returned.
 
 sub bodytag {
     my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,
-        $no_nav_bar,$bgcolor,$args,$advtoolsref,$ltiscope,$ltiuri,$ltimenu)=@_;
+        $no_nav_bar,$bgcolor,$args,$advtoolsref,$ltiscope,$ltiuri,
+        $ltimenu,$menucoll,$menuref)=@_;
 
     my $public;
     if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public'))
@@ -6088,12 +6156,24 @@ sub bodytag {
     if ($realm) {
         $realm = '/'.$realm;
     }
-    if ($role  eq 'ca') {
+    if ($role eq 'ca') {
         my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
         $realm = &plainname($rname,$rdom);
     } 
 # realm
+    my ($cid,$sec);
     if ($env{'request.course.id'}) {
+        $cid = $env{'request.course.id'};
+        if ($env{'request.course.sec'}) {
+            $sec = $env{'request.course.sec'};
+        }
+    } elsif ($realm =~ m{^/($match_domain)/($match_courseid)(?:|/(\w+))$}) {
+        if (&Apache::lonnet::is_course($1,$2)) {
+            $cid = $1.'_'.$2;
+            $sec = $3;
+        }
+    }
+    if ($cid) {
         if ($env{'request.role'} !~ /^cr/) {
             $role = &Apache::lonnet::plaintext($role,&course_type());
         } elsif ($role =~ m{^cr/($match_domain)/\1-domainconfig/(\w+)$}) {
@@ -6105,10 +6185,10 @@ sub bodytag {
         } else {
             $role = (split(/\//,$role,4))[-1]; 
         }
-        if ($env{'request.course.sec'}) {
-            $role .= ('&nbsp;'x2).'-&nbsp;'.&mt('section:').'&nbsp;'.$env{'request.course.sec'};
+        if ($sec) {
+            $role .= ('&nbsp;'x2).'-&nbsp;'.&mt('section:').'&nbsp;'.$sec;
         }   
-	$realm = $env{'course.'.$env{'request.course.id'}.'.description'};
+	$realm = $env{'course.'.$cid.'.description'};
     } else {
         $role = &Apache::lonnet::plaintext($role);
     }
@@ -6131,13 +6211,25 @@ sub bodytag {
 	undef($role);
     }
 
-    if (($env{'request.course.id'}) && ($env{'request.lti.login'})) {
+    my $showcrstitle = 1;
+    if (($cid) && ($env{'request.lti.login'})) {
         if (ref($ltimenu) eq 'HASH') {
             unless ($ltimenu->{'role'}) {
                 undef($role);
             }
             unless ($ltimenu->{'coursetitle'}) {
                 $realm='&nbsp;';
+                $showcrstitle = 0;
+            }
+        }
+    } elsif (($cid) && ($menucoll)) {
+        if (ref($menuref) eq 'HASH') {
+            unless ($menuref->{'role'}) {
+                undef($role);
+            }
+            unless ($menuref->{'crs'}) {
+                $realm='&nbsp;';
+                $showcrstitle = 0;
             }
         }
     }
@@ -6146,17 +6238,15 @@ sub bodytag {
     #
     # Extra info if you are the DC
     my $dc_info = '';
-    if ($env{'user.adv'} && exists($env{'user.role.dc./'.
-                        $env{'course.'.$env{'request.course.id'}.
-                                 '.domain'}.'/'})) {
-        my $cid = $env{'request.course.id'};
+    if (($env{'user.adv'}) && ($env{'request.course.id'}) && $showcrstitle &&
+        (exists($env{'user.role.dc./'.$env{'course.'.$cid.'.domain'}.'/'}))) {
         $dc_info = $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
         $dc_info =~ s/\s+$//;
     }
 
     my $crstype;
-    if ($env{'request.course.id'}) {
-        $crstype = $env{'course.'.$env{'request.course.id'}.'.type'};
+    if ($cid) {
+        $crstype = $env{'course.'.$cid.'.type'};
     } elsif ($args->{'crstype'}) {
         $crstype = $args->{'crstype'};
     }
@@ -6176,7 +6266,7 @@ sub bodytag {
             Apache::lonmenu::utilityfunctions($httphost), 'start');
 
         unless ($args->{'no_primary_menu'}) {
-            my ($left,$right) = Apache::lonmenu::primary_menu($crstype,$ltimenu);
+            my ($left,$right) = Apache::lonmenu::primary_menu($crstype,$ltimenu,$menucoll,$menuref);
 
             if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
                 if ($dc_info) {
@@ -6207,7 +6297,8 @@ sub bodytag {
         if (!$public){
             unless ($args->{'no_inline_menu'}) {
                 $bodytag .= Apache::lonmenu::secondary_menu($httphost,$ltiscope,$ltimenu,
-                                                            $args->{'no_primary_menu'});
+                                                            $args->{'no_primary_menu'},
+                                                            $menucoll,$menuref);
             }
             $bodytag .= Apache::lonmenu::serverform();
             $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
@@ -8543,10 +8634,17 @@ ADDMETA
                 unless (&Apache::lonnet::allowed('mau',$dom_in_use)) {
                     my %domdefs = &Apache::lonnet::get_domain_defaults($dom_in_use);
                     my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
-                    my $offload;
+                    my ($offload,$offloadoth);
                     if (ref($domdefs{'offloadnow'}) eq 'HASH') {
                         if ($domdefs{'offloadnow'}{$lonhost}) {
                             $offload = 1;
+                            if (($env{'user.domain'} ne '') && ($env{'user.domain'} ne $dom_in_use) &&
+                                (!(($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public')))) {
+                                unless (&Apache::lonnet::shared_institution($env{'user.domain'})) {
+                                    $offloadoth = 1;
+                                    $dom_in_use = $env{'user.domain'};
+                                }
+                            }
                         }
                     }
                     unless ($offload) {
@@ -8556,6 +8654,7 @@ ADDMETA
                                     (!(($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public')))) {
                                     unless (&Apache::lonnet::shared_institution($env{'user.domain'})) {
                                         $offload = 1;
+                                        $offloadoth = 1;
                                         $dom_in_use = $env{'user.domain'};
                                     }
                                 }
@@ -8563,7 +8662,13 @@ ADDMETA
                         }
                     }
                     if ($offload) {
-                        my $newserver = &Apache::lonnet::spareserver(30000,undef,1,$dom_in_use);
+                        my $newserver = &Apache::lonnet::spareserver(undef,30000,undef,1,$dom_in_use);
+                        if (($newserver eq '') && ($offloadoth)) {
+                            my @domains = &Apache::lonnet::current_machine_domains();
+                            if (($dom_in_use ne '') && (!grep(/^\Q$dom_in_use\E$/,@domains))) { 
+                                ($newserver) = &Apache::lonnet::choose_server($dom_in_use);
+                            }
+                        }
                         if (($newserver) && ($newserver ne $lonhost)) {
                             my $numsec = 5;
                             my $timeout = $numsec * 1000;
@@ -8577,7 +8682,7 @@ ADDMETA
                             }
                             if ($locknum) {
                                 my @lockinfo = sort(values(%locks));
-                                $msg = &mt('Once the following tasks are complete: ')."\n".
+                                $msg = &mt('Once the following tasks are complete:')." \n".
                                        join(", ",sort(values(%locks)))."\n";
                                 if (&show_course()) {
                                     $msg .= &mt('your session will be transferred to a different server, after you click "Courses".');
@@ -8870,7 +8975,7 @@ sub start_page {
     #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
 
     $env{'internal.start_page'}++;
-    my ($result,@advtools,$ltiscope,$ltiuri,%ltimenu);
+    my ($result,@advtools,$ltiscope,$ltiuri,%ltimenu,$menucoll,%menu);
 
     if (! exists($args->{'skip_phases'}{'head'}) ) {
         $result .= &xml_begin($args->{'frameset'}) . &headtag($title, $head_extra, $args);
@@ -8905,8 +9010,47 @@ sub start_page {
         ($ltiscope,$ltiuri) = &LONCAPA::ltiutils::lti_provider_scope($env{'request.lti.uri'},
                                   $env{'course.'.$env{'request.course.id'}.'.domain'},
                                   $env{'course.'.$env{'request.course.id'}.'.num'});
+    } elsif ($env{'request.course.id'}) {
+        my $expiretime=600;
+        if ((time-$env{'course.'.$env{'request.course.id'}.'.last_cache'}) > $expiretime) {
+            &Apache::lonnet::coursedescription($env{'request.course.id'},{'freshen_cache' => 1});
+        }
+        my ($deeplinkmenu,$menuref);
+        ($menucoll,$deeplinkmenu,$menuref) = &menucoll_in_effect();
+        if ($menucoll) {
+            if (ref($menuref) eq 'HASH') {
+                %menu = %{$menuref};
+            }
+            if ($menu{'top'} eq 'n') {
+                $args->{'no_primary_menu'} = 1;
+            }
+            if ($menu{'inline'} eq 'n') {
+                unless (&Apache::lonnet::allowed('opa')) {
+                    my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+                    my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+                    my $crstype = &course_type();
+                    my $now = time;
+                    my $ccrole;
+                    if ($crstype eq 'Community') {
+                        $ccrole = 'co';
+                    } else {
+                        $ccrole = 'cc';
+                    }
+                    if ($env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum}) {
+                        my ($start,$end) = split(/\./,$env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum});
+                        if ((($start) && ($start<0)) ||
+                            (($end) && ($end<$now))  ||
+                            (($start) && ($now<$start))) {
+                            $args->{'no_inline_menu'} = 1;
+                        }
+                    } else {
+                        $args->{'no_inline_menu'} = 1;
+                    }
+                }
+            }
+        }
     }
-    
+
     if (! exists($args->{'skip_phases'}{'body'}) ) {
 	if ($args->{'frameset'}) {
 	    my $attr_string = &make_attr_string($args->{'force_register'},
@@ -8919,7 +9063,7 @@ sub start_page {
                          $args->{'only_body'},      $args->{'domain'},
                          $args->{'force_register'}, $args->{'no_nav_bar'},
                          $args->{'bgcolor'},        $args,
-                         \@advtools,$ltiscope,$ltiuri,\%ltimenu);
+                         \@advtools,$ltiscope,$ltiuri,\%ltimenu,$menucoll,\%menu);
         }
     }
 
@@ -9005,6 +9149,93 @@ sub end_page {
     return $result;
 }
 
+sub menucoll_in_effect {
+    my ($menucoll,$deeplinkmenu,%menu);
+    if ($env{'request.course.id'}) {
+        $menucoll = $env{'course.'.$env{'request.course.id'}.'.menudefault'};
+        if ($env{'request.deeplink.login'}) {
+            my ($deeplink_symb,$deeplink);
+            my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+            my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+            if ($env{'request.noversionuri'} =~ m{^/(res|uploaded)/}) {
+                if ($env{'request.noversionuri'} =~ /\.(page|sequence)$/) {
+                    my $navmap = Apache::lonnavmaps::navmap->new();
+                    if (ref($navmap)) {
+                        $deeplink = $navmap->get_mapparam(undef,
+                                                          &Apache::lonnet::declutter($env{'request.noversionuri'}),
+                                                          '0.deeplink');
+                    }
+                } else {
+                    $deeplink = &Apache::lonnet::EXT('resource.0.deeplink');
+                }
+            } else {
+                $deeplink_symb = &deeplink_login_symb($cnum,$cdom);
+                if ($deeplink_symb =~ /\.(page|sequence)$/) {
+                    my $mapname = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($deeplink_symb))[2]);
+                    my $navmap = Apache::lonnavmaps::navmap->new();
+                    if (ref($navmap)) {
+                        $deeplink = $navmap->get_mapparam(undef,$mapname,'0.deeplink');
+                    }
+                } else {
+                    $deeplink = &Apache::lonnet::EXT('resource.0.deeplink',$deeplink_symb);
+                }
+            }
+            if ($deeplink ne '') {
+                my ($state,$others,$listed,$scope,$protect,$display) = split(/,/,$deeplink);
+                if ($display =~ /^\d+$/) {
+                    $deeplinkmenu = 1;
+                    $menucoll = $display;
+                }
+            }
+        }
+        if ($menucoll) {
+            %menu = &page_menu($env{'course.'.$env{'request.course.id'}.'.menucollections'},$menucoll);
+        }
+    }
+    return ($menucoll,$deeplinkmenu,\%menu);
+}
+
+sub deeplink_login_symb {
+    my ($cnum,$cdom) = @_;
+    my $login_symb;
+    if ($env{'request.deeplink.login'}) {
+        $login_symb = &symb_from_tinyurl($env{'request.deeplink.login'},$cnum,$cdom);
+    }
+    return $login_symb;
+}
+
+sub symb_from_tinyurl {
+    my ($url,$cnum,$cdom) = @_;
+    if ($url =~ m{^\Q/tiny/$cdom/\E(\w+)$}) {
+        my $key = $1;
+        my ($tinyurl,$login);
+        my ($result,$cached)=&Apache::lonnet::is_cached_new('tiny',$cdom."\0".$key);
+        if (defined($cached)) {
+            $tinyurl = $result;
+        } else {
+            my $configuname = &Apache::lonnet::get_domainconfiguser($cdom);
+            my %currtiny = &Apache::lonnet::get('tiny',[$key],$cdom,$configuname);
+            if ($currtiny{$key} ne '') {
+                $tinyurl = $currtiny{$key};
+                &Apache::lonnet::do_cache_new('tiny',$cdom."\0".$key,$currtiny{$key},600);
+            }
+        }
+        if ($tinyurl ne '') {
+            my ($cnumreq,$symb) = split(/\&/,$tinyurl);
+            if (wantarray) {
+                return ($cnumreq,$symb);
+            } elsif ($cnumreq eq $cnum) {
+                return $symb;
+            }
+        }
+    }
+    if (wantarray) {
+        return ();
+    } else {
+        return;
+    }
+}
+
 sub wishlist_window {
     return(<<'ENDWISHLIST');
 <script type="text/javascript">
@@ -9094,7 +9325,15 @@ ENDLINK
 }
 
 sub modal_adhoc_script {
-    my ($funcname,$width,$height,$content)=@_;
+    my ($funcname,$width,$height,$content,$possmathjax)=@_;
+    my $mathjax;
+    if ($possmathjax) {
+        $mathjax = <<'ENDJAX';
+               if (typeof MathJax == 'object') {
+                   MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
+               }
+ENDJAX
+    }
     return (<<ENDADHOC);
 <script type="text/javascript">
 // <![CDATA[
@@ -9105,6 +9344,7 @@ sub modal_adhoc_script {
                 modalWindow.height = $height;
                 modalWindow.content = '$content';
                 modalWindow.open();
+                $mathjax
         };  
 // ]]>
 </script>
@@ -9112,7 +9352,7 @@ ENDADHOC
 }
 
 sub modal_adhoc_inner {
-    my ($funcname,$width,$height,$content)=@_;
+    my ($funcname,$width,$height,$content,$possmathjax)=@_;
     my $innerwidth=$width-20;
     $content=&js_ready(
                  &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}).
@@ -9121,12 +9361,12 @@ sub modal_adhoc_inner {
                  &end_scrollbox().
                  &end_page()
              );
-    return &modal_adhoc_script($funcname,$width,$height,$content);
+    return &modal_adhoc_script($funcname,$width,$height,$content,$possmathjax);
 }
 
 sub modal_adhoc_window {
-    my ($funcname,$width,$height,$content,$linktext)=@_;
-    return &modal_adhoc_inner($funcname,$width,$height,$content).
+    my ($funcname,$width,$height,$content,$linktext,$possmathjax)=@_;
+    return &modal_adhoc_inner($funcname,$width,$height,$content,$possmathjax).
            "<a href=\"javascript:$funcname();void(0);\">".$linktext."</a>";
 }
 
@@ -11001,11 +11241,15 @@ sub sorted_inst_types {
 }
 
 sub get_institutional_codes {
-    my ($settings,$allcourses,$LC_code) = @_;
+    my ($cdom,$crs,$settings,$allcourses,$LC_code) = @_;
 # Get complete list of course sections to update
     my @currsections = ();
     my @currxlists = ();
+    my (%unclutteredsec,%unclutteredlcsec);
     my $coursecode = $$settings{'internal.coursecode'};
+    my $crskey = $crs.':'.$coursecode;
+    @{$unclutteredsec{$crskey}} = ();
+    @{$unclutteredlcsec{$crskey}} = ();
 
     if ($$settings{'internal.sectionnums'} ne '') {
         @currsections = split(/,/,$$settings{'internal.sectionnums'});
@@ -11016,8 +11260,8 @@ sub get_institutional_codes {
     }
 
     if (@currxlists > 0) {
-        foreach (@currxlists) {
-            if (m/^([^:]+):(\w*)$/) {
+        foreach my $xl (@currxlists) {
+            if ($xl =~ /^([^:]+):(\w*)$/) {
                 unless (grep/^$1$/,@{$allcourses}) {
                     push(@{$allcourses},$1);
                     $$LC_code{$1} = $2;
@@ -11025,15 +11269,28 @@ sub get_institutional_codes {
             }
         }
     }
- 
+
     if (@currsections > 0) {
-        foreach (@currsections) {
-            if (m/^(\w+):(\w*)$/) {
-                my $sec = $coursecode.$1;
+        foreach my $sec (@currsections) {
+            if ($sec =~ m/^(\w+):(\w*)$/ ) {
+                my $instsec = $1;
                 my $lc_sec = $2;
-                unless (grep/^$sec$/,@{$allcourses}) {
+                unless (grep/^\Q$instsec\E$/,@{$unclutteredsec{$crskey}}) {
+                    push(@{$unclutteredsec{$crskey}},$instsec);
+                    push(@{$unclutteredlcsec{$crskey}},$lc_sec);
+                }
+            }
+        }
+    }
+
+    if (@{$unclutteredsec{$crskey}} > 0) {
+        my %formattedsec = &Apache::lonnet::auto_instsec_reformat($cdom,'clutter',\%unclutteredsec);
+        if ((ref($formattedsec{$crskey}) eq 'ARRAY') && (ref($unclutteredlcsec{$crskey}) eq 'ARRAY')) {
+            for (my $i=0; $i<@{$formattedsec{$crskey}}; $i++) {
+                my $sec = $coursecode.$formattedsec{$crskey}[$i];
+                unless (grep/^\Q$sec\E$/,@{$allcourses}) {
                     push(@{$allcourses},$sec);
-                    $$LC_code{$sec} = $lc_sec;
+                    $$LC_code{$sec} = $unclutteredlcsec{$crskey}[$i];
                 }
             }
         }
@@ -16153,8 +16410,7 @@ sub construct_course {
                    'plc.users.denied',
                    'hidefromcat',
                    'checkforpriv',
-                   'categories',
-                   'internal.uniquecode'],
+                   'categories'],
                    $$crsudom,$$crsunum);
         if ($args->{'textbook'}) {
             $cenv{'internal.textbook'} = $args->{'textbook'};
@@ -16748,6 +17004,7 @@ sub init_user_environment {
 # --------------------------------------------------------- Write first profile
 
     {
+        my $ip = &Apache::lonnet::get_requestor_ip($r);
 	my %initial_env = 
 	    ("user.name"          => $username,
 	     "user.domain"        => $domain,
@@ -16766,7 +17023,7 @@ sub init_user_environment {
 	     "request.course.sec" => '',
 	     "request.role"       => 'cm',
 	     "request.role.adv"   => $env{'user.adv'},
-	     "request.host"       => $ENV{'REMOTE_ADDR'},);
+	     "request.host"       => $ip,);
 
         if ($form->{'localpath'}) {
 	    $initial_env{"browser.localpath"}  = $form->{'localpath'};
@@ -18105,11 +18362,12 @@ sub create_recaptcha {
 sub check_recaptcha {
     my ($privkey,$version) = @_;
     my $captcha_chk;
+    my $ip = &Apache::lonnet::get_requestor_ip();
     if ($version >= 2) {
         my %info = (
                      secret   => $privkey, 
                      response => $env{'form.g-recaptcha-response'},
-                     remoteip => $ENV{'REMOTE_ADDR'},
+                     remoteip => $ip,
                    );
         my $request=new HTTP::Request('POST','https://www.google.com/recaptcha/api/siteverify');
         $request->content(join('&',map {
@@ -18132,7 +18390,7 @@ sub check_recaptcha {
         my $captcha_result =
             $captcha->check_answer(
                                     $privkey,
-                                    $ENV{'REMOTE_ADDR'},
+                                    $ip,
                                     $env{'form.recaptcha_challenge_field'},
                                     $env{'form.recaptcha_response_field'},
                                   );
@@ -18184,6 +18442,9 @@ sub cleanup_html {
 # $context is the calling context -- roles, grades, contents, menu or flip. 
 sub critical_redirect {
     my ($interval,$context) = @_;
+    unless (($env{'user.domain'} ne '') && ($env{'user.name'} ne '')) {
+        return ();
+    }
     if ((time-$env{'user.criticalcheck.time'})>$interval) {
         if (($env{'request.course.id'}) && (($context eq 'flip') || ($context eq 'contents'))) {
             my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
@@ -18205,7 +18466,7 @@ sub critical_redirect {
         &Apache::lonnet::appenv({'user.criticalcheck.time'=>time});
         my $redirecturl;
         if ($what[0]) {
-	    if (($what[0] ne 'con_lost') && ($what[0]!~/^error\:/)) {
+	    if (($what[0] ne 'con_lost') && ($what[0] ne 'no_such_host') && ($what[0]!~/^error\:/)) {
 	        $redirecturl='/adm/email?critical=display';
 	        my $url=&Apache::lonnet::absolute_url().$redirecturl;
                 return (1, $url);
@@ -18567,6 +18828,37 @@ sub is_nonframeable {
     return $uselink;
 }
 
+sub page_menu {
+    my ($menucolls,$menunum) = @_;
+    my %menu;
+    foreach my $item (split(/;/,$menucolls)) {
+        my ($num,$value) = split(/\%/,$item);
+        if ($num eq $menunum) {
+            my @entries = split(/\&/,$value);
+            foreach my $entry (@entries) {
+                my ($name,$fields) = split(/=/,$entry);
+                if (($name eq 'top') || ($name eq 'inline') || ($name eq 'main')) {
+                    $menu{$name} = $fields;
+                } else {
+                    my @shown;
+                    if ($fields =~ /,/) {
+                        @shown = split(/,/,$fields);
+                    } else {
+                        @shown = ($fields);
+                    }
+                    if (@shown) {
+                        foreach my $field (@shown) {
+                            next if ($field eq '');
+                            $menu{$field} = 1;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return %menu;
+}
+
 1;
 __END__;