--- loncom/interface/loncommon.pm	2024/03/03 00:08:37	1.1427
+++ loncom/interface/loncommon.pm	2025/02/25 16:33:39	1.1464
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.1427 2024/03/03 00:08:37 raeburn Exp $
+# $Id: loncommon.pm,v 1.1464 2025/02/25 16:33:39 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -1174,7 +1174,8 @@ sub linked_select_forms {
         $menuorder,
         $onchangefirst,
         $onchangesecond,
-        $suffix
+        $suffix,
+        $haslabel
         ) = @_;
     my $second = "document.$formname.$secondselectname";
     my $first = "document.$formname.$firstselectname";
@@ -1240,13 +1241,18 @@ END
         $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
     }
     $result .= "</select>\n";
+    if ($haslabel) {
+        $result .= '</label>';
+    }
     my %select2;
     if (ref($hashref->{$firstdefault}) eq 'HASH') {
         if (ref($hashref->{$firstdefault}->{'select2'}) eq 'HASH') {
             %select2 = %{$hashref->{$firstdefault}->{'select2'}};
         }
     }
-    $result .= $middletext;
+    if ($middletext ne '') {
+        $result .= '<label>'.$middletext;
+    }
     $result .= "<select size=\"1\" name=\"$secondselectname\"";
     if ($onchangesecond) {
         $result .= ' onchange="'.$onchangesecond.'"';
@@ -1264,6 +1270,9 @@ END
         $result.=">".&mt($select2{$value})."</option>\n";
     }
     $result .= "</select>\n";
+    if ($middletext ne '') {
+        $result .= '</label>';
+    }
     #    return $debug;
     return $result;
 }   #  end of sub linked_select_forms {
@@ -1451,7 +1460,7 @@ $banner_link
 <a href="$link" title="$title" $linkattr>$text</a>
 END
     } else {
-        return '&nbsp;'.$text.'&nbsp;';
+        return '&nbsp;<h1 class="LC_helpmenu">'.$text.'</h1>&nbsp;';
     }
 }
 
@@ -1463,6 +1472,10 @@ sub help_menu_js {
     my $helptopic=&general_help();
     my $details_link = $httphost.'/adm/help/'.$helptopic.'.hlp';
     my $nothing=&Apache::lonhtmlcommon::javascript_nothing();
+    my $bannertitle = &mt('Help Menu');
+    &js_escape(\$bannertitle);
+    my $bodytitle = &mt('Documentation');
+    &js_escape(\$bodytitle);
     my $start_page =
         &Apache::loncommon::start_page('Help Menu', undef,
 				       {'frameset'    => 1,
@@ -1474,7 +1487,6 @@ sub help_menu_js {
     my $end_page =
         &Apache::loncommon::end_page({'frameset' => 1,
 				      'js_ready' => 1,});
-
     my $template .= <<"ENDTEMPLATE";
 <script type="text/javascript">
 // <![CDATA[
@@ -1499,8 +1511,8 @@ function helpMenu(target) {
     return;
 }
 function writeHelp(caller) {
-    caller.document.writeln('$start_page\\n<frame name="bannerframe" src="'+banner_link+'" marginwidth="0" marginheight="0" frameborder="0">\\n');
-    caller.document.writeln('<frame name="bodyframe" src="$details_link" marginwidth="0" marginheight="0" frameborder="0">\\n$end_page');
+    caller.document.writeln('$start_page\\n<frame name="bannerframe" title="$bannertitle" src="'+banner_link+'" marginwidth="0" marginheight="0" frameborder="0">\\n');
+    caller.document.writeln('<frame name="bodyframe" title="$bodytitle" src="$details_link" marginwidth="0" marginheight="0" frameborder="0">\\n$end_page');
     caller.document.close();
     caller.focus();
 }
@@ -2300,7 +2312,7 @@ sub import_crsauthor_form {
         dire => 'Directory',
         se   => 'Select',
     );
-    $output = $lt{'dire'}.':&nbsp;'.
+    $output = '<label>'.$lt{'dire'}.':&nbsp;'.
               '<select id="'.$firstselectname.'" name="'.$firstselectname.'" '.
               'onchange="populateCrsSelects(this.form,'."'$firstselectname','$secondselectname',1,'$js_only',0,1,0,0,0".');">'.
               '<option value="" selected="selected">'.$lt{'se'}.'</option>';
@@ -2311,10 +2323,10 @@ sub import_crsauthor_form {
         next if ($key eq '/');
         $output .= '<option value="'.$key.'">'.$key.'</option>'."\n";
     }
-    $output .= '</select><br />'."\n".
+    $output .= '</select></label><br /><label>'."\n".
                $lt{'fnam'}.':&nbsp;<select id="'.$secondselectname.'" name="'.$secondselectname.'">'."\n".
                '<option value="" selected="selected"></option>'."\n".
-               '</select>'."\n".
+               '</select></label>'."\n".
                '<input type="hidden" id="crsres_include_'.$suffix.'" value="'.$only.'" />';
     return ($numdirs,$output);
 }
@@ -2476,7 +2488,7 @@ END
 <domain>$cdom</domain>
 <highestgradelevel>0</highestgradelevel>
 <keywords></keywords>
-<language>notset </language>
+<language>notset</language>
 <lastrevisiondate>$now</lastrevisiondate>
 <lowestgradelevel>0</lowestgradelevel>
 <mime>rights</mime>
@@ -3066,7 +3078,7 @@ sub select_level_form {
 
 =pod
 
-=item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms,$disabled)
+=item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms,$disabled,$id)
 
 Returns a string containing a <select name='$name' size='1'> form to 
 allow a user to select the domain to preform an operation in.  
@@ -3085,17 +3097,22 @@ The optional $excdoms is a reference to
 
 The optional $disabled argument, if true, adds the disabled attribute to the select tag.
 
+The option $id argument is the value (if any) to set as the (unique) id attribute for the select tag.
+
 =cut
 
 #-------------------------------------------
 sub select_dom_form {
-    my ($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms,$disabled) = @_;
+    my ($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms,$disabled,$id) = @_;
     if ($onchange) {
         $onchange = ' onchange="'.$onchange.'"';
     }
     if ($disabled) {
         $disabled = ' disabled="disabled"';
     }
+    if ($id ne '') {
+        $id = ' id="'.$id.'"';
+    }
     my (@domains,%exclude);
     if (ref($incdoms) eq 'ARRAY') {
         @domains = sort {lc($a) cmp lc($b)} (@{$incdoms});
@@ -3106,7 +3123,7 @@ sub select_dom_form {
     if (ref($excdoms) eq 'ARRAY') {
         map { $exclude{$_} = 1; } @{$excdoms}; 
     }
-    my $selectdomain = "<select name=\"$name\" size=\"1\"$onchange$disabled>\n";
+    my $selectdomain = "<select name=\"$name\" size=\"1\"$onchange$disabled$id>\n";
     foreach my $dom (@domains) {
         next if ($exclude{$dom});
         $selectdomain.="<option value=\"$dom\" ".
@@ -3950,6 +3967,21 @@ sub passwd_validation_js {
         } else {
             $alertmsg = &mt('A secret did not satisfy requirement(s):').'\n\n';
         }
+    } elsif ($context eq 'ltitools') {
+        my %domconfig = &Apache::lonnet::get_dom('configuration',['toolsec'],$domain);
+        if (ref($domconfig{'toolsec'}) eq 'HASH') {
+            if (ref($domconfig{'toolsec'}{'rules'}) eq 'HASH') {
+                %passwdconf = %{$domconfig{'toolsec'}{'rules'}};
+            }
+        }
+        if ($id eq 'add') {
+            $alertmsg = &mt('Secret for added external tool did not satisfy requirement(s):').'\n\n';
+        } elsif ($id =~ /^\d+$/) {
+            my $pos = $id+1;
+            $alertmsg = &mt('Secret for external tool [_1] did not satisfy requirement(s):','#'.$pos).'\n\n';
+        } else {
+            $alertmsg = &mt('A secret did not satisfy requirement(s):').'\n\n';
+        }
     } else {
         %passwdconf = &Apache::lonnet::get_passwdconf($domain);
         $alertmsg = &mt('Initial password did not satisfy requirement(s):').'\n\n';
@@ -5313,6 +5345,7 @@ sub get_student_view {
   $userview=~s/\<\/html\>//gi;
   $userview=~s/\<head\>//gi;
   $userview=~s/\<\/head\>//gi;
+  $userview=~s/\Q<div class="LC_landmark" role="main"\E/<div class="LC_landmark"/; 
   $userview=~s/action\s*\=/would_be_action\=/gi;
   $userview=&relative_to_absolute($feedurl,$userview);
   if (wantarray) {
@@ -5676,7 +5709,8 @@ sub blockcheck {
             }
             unless ($has_evb) {
                 if (($activity eq 'printout') || ($activity eq 'grades') || ($activity eq 'search') ||
-                    ($activity eq 'boards') || ($activity eq 'groups') || ($activity eq 'chat')) {
+                    ($activity eq 'index') || ($activity eq 'boards') || ($activity eq 'groups') || 
+                    ($activity eq 'chat')) {
                     if ($udom eq $cdom) {
                         $check_ipaccess = 1;
                     }
@@ -5767,8 +5801,8 @@ sub blockcheck {
 
     if (($activity eq 'boards' || $activity eq 'chat' ||
          $activity eq 'groups' || $activity eq 'printout' ||
-         $activity eq 'search' || $activity eq 'reinit' ||
-         $activity eq 'alert') &&
+         $activity eq 'search' || $activity eq 'index' ||
+         $activity eq 'reinit' || $activity eq 'alert') &&
         ($env{'request.course.id'})) {
         foreach my $key (keys(%live_courses)) {
             if ($key ne $env{'request.course.id'}) {
@@ -6103,6 +6137,8 @@ END_MYBLOCK
         $text = &mt('Gradebook Blocked');
     } elsif ($activity eq 'search') {
         $text = &mt('Search Blocked');
+    } elsif ($activity eq 'index') {
+        $text = &mt('Content Index Blocked');
     } elsif ($activity eq 'alert') {
         $text = &mt('Checking Critical Messages Blocked');
     } elsif ($activity eq 'reinit') {
@@ -6536,7 +6572,7 @@ Input: (optional) filename from which br
        If page header is being requested for use in a frameset, then
        the second (option) argument -- frameset will be true, and
        the target attribute set for links should be target="_parent".
-       If $title is supplied as the thitd arg, that will be used to 
+       If $title is supplied as the third arg, that will be used to 
        the left of the breadcrumbs tail for the current path.
 
 Returns: HTML div with CSTR path and recent box
@@ -6656,20 +6692,32 @@ sub nocodemirror {
 Input: $uri (optional)
 
 Returns: %editors hash in which keys are editors
-         permitted in current Authoring Space.
+         permitted in current Authoring Space,
+         or in current course for web pages
+         created in a course.
+
          Value for each key is 1. Possible keys
-         are: edit, xml, and daxe. If no specific
+         are: edit, xml, and daxe.
+
+         For a regular Authoring Space, if no specific
          set of editors has been set for the Author
          who owns the Authoring Space, then the
          domain default will be used.  If no domain
          default has been set, then the keys will be
          edit and xml.
 
+         For a course author, or for web pages created
+         in a course, if no specific set of editors has
+         been set for the course, then the domain
+         course default will be used. If no domain
+         course default has been set, then the keys
+         will be edit and xml.
+
 =cut
 
 sub permitted_editors {
     my ($uri) = @_;
-    my ($is_author,$is_coauthor,$auname,$audom,%editors);
+    my ($is_author,$is_coauthor,$is_course,$auname,$audom,%editors);
     if ($env{'request.role'} =~ m{^au\./}) {
         $is_author = 1;
     } elsif ($env{'request.role'} =~ m{^(?:ca|aa)\./($match_domain)/($match_username)}) {
@@ -6683,20 +6731,33 @@ sub permitted_editors {
             }
         }
     } elsif ($env{'request.course.id'}) {
-        if ($env{'request.editurl'} =~ m{^/priv/($match_domain)/($match_username)/}) {
+        my ($cdom,$cnum);
+        $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+        $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+        if (($env{'request.editurl'} =~ m{^/priv/\Q$cdom/$cnum\E/}) ||
+            ($env{'request.editurl'} =~ m{^/uploaded/\Q$cdom/$cnum\E/(docs|supplemental)/}) ||
+            ($uri =~ m{^/uploaded/\Q$cdom/$cnum\E/(docs|supplemental)/})) {
+            $is_course = 1;
+        } elsif ($env{'request.editurl'} =~ m{^/priv/($match_domain)/($match_username)/}) {
             ($audom,$auname) = ($1,$2);
         } elsif ($env{'request.uri'} =~ m{^/priv/($match_domain)/($match_username)/}) {
             ($audom,$auname) = ($1,$2);
         } elsif (($uri eq '/daxesave') &&
+                 (($env{'form.path'} =~ m{^/daxeopen/priv/\Q$cdom/$cnum\E/}) ||
+                  ($env{'form.path'} =~ m{^/daxeopen/uploaded/\Q$cdom/$cnum\E/(docs|supplemental)/}))) {
+            $is_course = 1;
+        } elsif (($uri eq '/daxesave') &&
                  ($env{'form.path'} =~ m{^/daxeopen/priv/($match_domain)/($match_username)/})) {
             ($audom,$auname) = ($1,$2);
         }
-        if (($audom ne '') && ($auname ne '')) {
-            if (($env{'user.domain'} eq $audom) &&
-                ($env{'user.name'} eq $auname)) {
-                $is_author = 1;
-            } else {
-                $is_coauthor = 1;
+        unless ($is_course) {
+            if (($audom ne '') && ($auname ne '')) {
+                if (($env{'user.domain'} eq $audom) &&
+                    ($env{'user.name'} eq $auname)) {
+                    $is_author = 1;
+                } else {
+                    $is_coauthor = 1;
+                }
             }
         }
     }
@@ -6716,6 +6777,19 @@ sub permitted_editors {
                          xml => 1,
                        );
         }
+    } elsif ($is_course) {
+        if (exists($env{'course.'.$env{'request.course.id'}.'.internal.crseditors'})) {
+            map { $editors{$_} = 1; } split(/,/,$env{'course.'.$env{'request.course.id'}.'.internal.crseditors'});
+        } else {
+            my %domdefaults = &Apache::lonnet::get_domain_defaults($env{'course.'.$env{'request.course.id'}.'.domain'});
+            if (exists($domdefaults{'crseditors'})) {
+                map { $editors{$_} = 1; } split(/,/,$domdefaults{'crseditors'});
+            } else {
+                %editors = ( edit => 1,
+                             xml => 1,
+                           );
+            }
+        }
     } else {
         %editors = ( edit => 1,
                      xml => 1,
@@ -6824,7 +6898,6 @@ sub bodytag {
     my $hostname = $args->{'hostname'};
 
     $function = &get_users_function() if (!$function);
-    my $img =    &designparm($function.'.img',$domain);
     my $font =   &designparm($function.'.font',$domain);
     my $pgbg   = $bgcolor || &designparm($function.'.pgbg',$domain);
 
@@ -6878,8 +6951,6 @@ sub bodytag {
         $role = &Apache::lonnet::plaintext($role);
     }
 
-    if (!$realm) { $realm='&nbsp;'; }
-
     my $extra_body_attr = &make_attr_string($forcereg,\%design);
 
 # construct main body tag
@@ -6903,7 +6974,6 @@ sub bodytag {
                 undef($role);
             }
             unless ($ltimenu->{'coursetitle'}) {
-                $realm='&nbsp;';
                 $showcrstitle = 0;
             }
         }
@@ -6913,7 +6983,6 @@ sub bodytag {
                 undef($role);
             }
             unless ($menuref->{'crs'}) {
-                $realm='&nbsp;';
                 $showcrstitle = 0;
             }
         }
@@ -6947,8 +7016,12 @@ sub bodytag {
         #        $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls
         #    }
 
-        $bodytag .= Apache::lonhtmlcommon::scripttag(
-            Apache::lonmenu::utilityfunctions($httphost), 'start');
+        my $need_endlcint;
+        unless ($args->{'switchserver'}) {
+            $bodytag .= Apache::lonhtmlcommon::scripttag(
+                Apache::lonmenu::utilityfunctions($httphost), 'start');
+            $need_endlcint = 1;
+        }
 
         my $collapsible;
         if ($args->{'collapsible_header'} ne '') {
@@ -6966,7 +7039,7 @@ sub bodytag {
             my $alttext = &mt('menu state: '.$menustate);
             my $tooltip = &mt($tiptext.' standard menus');
             $bodytag .= <<"END";
-<div id="LC_expandingContainer" style="display:inline;">
+<div id="LC_expandingContainer" style="display:inline;" role="navigation">
 <div id="LC_collapsible" class="LC_collapse_trigger" style="position: absolute;top: -5px;left: 0px; z-index:101; display:inline;">
 <a href="#" style="text-decoration:none;"><img class="LC_collapsible_indicator" alt="$alttext" title="$tooltip" src="/res/adm/pages/$menustate.png" style="border:0;margin:0;padding:0;max-width:100%;height:auto" /></a></div>
 <div class="LC_menus_content $divclass">
@@ -6977,43 +7050,59 @@ END
                                                               $args->{'links_disabled'},
                                                               $args->{'links_target'},
                                                               $collapsible);
-
+            my $labeltext = &HTML::Entities::encode(&mt('Primary links'));
             if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
                 if ($dc_info) {
                     $dc_info = qq|<span class="LC_cusr_subheading">$dc_info</span>|;
                 }
-                $bodytag .= qq|<div id="LC_nav_bar">$left $role<br />
-                               <em>$realm</em> $dc_info</div>|;
+                $bodytag .= qq|<div id="LC_nav_bar" role="navigation" aria-label="$labeltext">$left $role</div>|;
+                unless (($realm eq '') && ($dc_info eq '')) {
+                    $bodytag .= qq|<div id="LC_realm" role="complementary"><em>$realm</em> $dc_info</div>|;
+                }
+                if ($need_endlcint) {
+                    $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
+                }
                 return $bodytag;
             }
 
+            $bodytag .= '<div class="LC_landmark" style="margin: 3px 0 0 0;" role="navigation" aria-label="'.$labeltext.'">';
             unless ($env{'request.symb'} =~ m/\.page___\d+___/) {
                 $bodytag .= qq|<div id="LC_nav_bar">$left $role</div>|;
             }
 
-            $bodytag .= $right;
+            $bodytag .= $right.'</div>';
 
             if ($dc_info) {
                 $dc_info = &dc_courseid_toggle($dc_info);
             }
-            $bodytag .= qq|<div id="LC_realm">$realm $dc_info</div>|;
+            unless (($realm eq '') && ($dc_info eq '')) {
+                $bodytag .= qq|<div id="LC_realm" role="complementary">$realm $dc_info</div>|;
+            }
+            $bodytag .= qq|<div style="clear: both; margin: 5px 0 0 0;"></div>|;
         }
 
         #if directed to not display the secondary menu, don't.  
         if ($args->{'no_secondary_menu'}) {
+            if ($need_endlcint) {
+                $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
+            }
             return $bodytag;
         }
         #don't show menus for public users
         if (!$public){
             unless ($args->{'no_inline_menu'}) {
-                $bodytag .= Apache::lonmenu::secondary_menu($httphost,$ltiscope,$ltimenu,
+                $bodytag .= '<div class="LC_landmark" role="navigation" aria-label="Secondary Links">'.
+                            Apache::lonmenu::secondary_menu($httphost,$ltiscope,$ltimenu,
                                                             $args->{'no_primary_menu'},
                                                             $menucoll,$menuref,
                                                             $args->{'links_disabled'},
-                                                            $args->{'links_target'});
+                                                            $args->{'links_target'}).
+                            '</div>';
             }
             $bodytag .= Apache::lonmenu::serverform();
-            $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
+            if ($need_endlcint) {
+                $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
+            }
             if ($env{'request.state'} eq 'construct') {
                 $bodytag .= &Apache::lonmenu::innerregister($forcereg,
                                 $args->{'bread_crumbs'},'','',$hostname,
@@ -7023,17 +7112,19 @@ END
                                 $args->{'group'},$args->{'hide_buttons'},
                                 $hostname,$ltiscope,$ltiuri,$showncrumbsref);
             } else {
-                $bodytag .= 
+                $bodytag .=
                     &Apache::lonmenu::prepare_functions($env{'request.noversionuri'},
                                                         $forcereg,$args->{'group'},
                                                         $args->{'bread_crumbs'},
                                                         $advtoolsref,'',$hostname);
             }
-        }else{
-            # this is to seperate menu from content when there's no secondary
-            # menu. Especially needed for public accessible ressources.
-            $bodytag .= '<hr style="clear:both" />';
-            $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); 
+        } else {
+            # this is to separate menu from content when there's no secondary
+            # menu. Especially needed for publicly accessible resources.
+            $bodytag .= '<hr style="clear:both" role="complementary" />';
+            if ($need_endlcint) {
+                $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
+            }
         }
         if ($args->{'collapsible_header'} ne '') {
             $bodytag .= $args->{'collapsible_header'}.
@@ -7168,7 +7259,6 @@ Inputs: (all optional)
 sub standard_css {
     my ($function,$domain,$bgcolor) = @_;
     $function  = &get_users_function() if (!$function);
-    my $img    = &designparm($function.'.img',   $domain);
     my $tabbg  = &designparm($function.'.tabbg', $domain);
     my $font   = &designparm($function.'.font',  $domain);
     my $fontmenu = &designparm($function.'.fontmenu', $domain);
@@ -7221,6 +7311,7 @@ body {
   line-height:130%;
   font-size:0.83em;
   color:$font;
+  background-color: $pgbg_or_bgcolor;
 }
 
 a:focus,
@@ -7232,8 +7323,32 @@ form, .inline {
   display: inline;
 }
 
+.LC_landmark {
+  margin: 0;
+  padding: 0;
+  border: none;
+}
+
+.LC_visually_hidden:not(:focus):not(:active) {
+    clip-path: inset(50%);
+    height: 1px;
+    overflow: hidden;
+    position: absolute;
+    white-space: nowrap;
+    width: 1px;
+    display: inline;
+}
+
+.LC_heading_2 {
+  font-size: 1.17em;
+}
+
+.LC_heading_3 {
+  font-size: 1.0em;
+}
+
 .LC_menus_content.shown{
-  display: inline;
+  display: block;
 }
 
 .LC_menus_content.hidden {
@@ -7244,6 +7359,10 @@ form, .inline {
   text-align:right;
 }
 
+.LC_center {
+  text-align:center;
+}
+
 .LC_middle {
   vertical-align:middle;
 }
@@ -7886,39 +8005,68 @@ table.LC_parm_overview_restrictions th {
   border-color: $pgbg;
 }
 
-table#LC_helpmenu {
-  border: none;
-  height: 55px;
-  border-spacing: 0;
+h1.LC_helpmenu {
+  display: inline;
+  font-size: 100%;
+  font-weight: normal;
+  line-height: 1em;
+  margin: 0;
+  padding: 0;
+  border: 0;
 }
 
-table#LC_helpmenu fieldset legend {
-  font-size: larger;
+.LC_helpdesk_headbox {
+  border: 2px groove threedface;
+  padding: 1em;
 }
 
-table#LC_helpmenu_links {
-  width: 100%;
-  border: 1px solid black;
+h1.LC_helpdesk_legend {
+  float: left;
+  margin: -1.7em 0 0;
+  padding: 0 .5em;
   background: $pgbg;
+  font-size: 1em;
+  font-weight: bold;
+}
+
+h1.LC_helpdesk_title {
+  display: inline;
+  font-size: 1em;
+  line-height: 2.5em;
+  margin: 0;
   padding: 0;
-  border-spacing: 1px;
+  vertical-align: bottom;
 }
 
-table#LC_helpmenu_links tr td {
-  padding: 1px;
+.LC_helpdesk_links {
+  border: 1px solid black;
+  padding: 3px;
   background: $tabbg;
   text-align: center;
   font-weight: bold;
+  display: inline;
+  margin-right: -6px;
 }
 
-table#LC_helpmenu_links a:link,
-table#LC_helpmenu_links a:visited,
-table#LC_helpmenu_links a:active {
+.LC_helpdesk_img,
+.LC_helpdesk_text {
+  padding: 0;
+  margin: 0;
+  border: 0;
+  display: inline;
+}
+
+.LC_helpdesk_img a:link,
+.LC_helpdesk_img a:visited,
+.LC_helpdesk_img a:active,
+.LC_helpdesk_text a:link,
+.LC_helpdesk_text a:visited,
+.LC_helpdesk_text a:active {
   text-decoration: none;
   color: $font;
 }
 
-table#LC_helpmenu_links a:hover {
+div.LC_helpdesk_text a:hover {
   text-decoration: underline;
   color: $vlink;
 }
@@ -7945,7 +8093,7 @@ table.LC_pick_box {
   border-spacing: 1px;
 }
 
-table.LC_pick_box td.LC_pick_box_title {
+table.LC_pick_box th.LC_pick_box_title {
   background: $sidebg;
   font-weight: bold;
   text-align: left;
@@ -8148,6 +8296,29 @@ table.LC_prior_tries td {
   padding: 6px;
 }
 
+.LC_prob_status {
+  margin-top: 5px;
+  padding-top: 0;
+  padding-left: 0;
+  padding-bottom: 0;
+  padding-right: 5px;
+}
+
+.LC_mail_actions {
+  float: left;
+  padding: 0;
+  margin: 6px;
+}
+
+.LC_vertical_line {
+  width: 1px;
+  background-color: black;
+  height: 4em;
+  float: left;
+  margin: 0;
+  padding: 0;
+}
+
 span.LC_prior_numerical,
 span.LC_prior_string,
 span.LC_prior_custom,
@@ -8301,6 +8472,10 @@ div.LC_grade_show_user div.LC_Box {
   margin-right: 50px;
 }
 
+div.LC_grade_show_user div.LC_Box table tr th {
+  font-weight: normal;
+}
+
 div.LC_grade_submissions,
 div.LC_grade_message_center,
 div.LC_grade_info_links {
@@ -8331,6 +8506,12 @@ table.LC_scantron_action tr th {
   font-style:normal;
 }
 
+div.LC_edit_problem_daxe_header {
+  padding: 3px;
+  background: $tabbg;
+  z-index: 100;
+}
+
 .LC_edit_problem_header,
 div.LC_edit_problem_footer {
   font-weight: normal;
@@ -8396,8 +8577,9 @@ img.stift {
   vertical-align: middle;
 }
 
-table td.LC_mainmenu_col_fieldset {
-  vertical-align: top;
+div.LC_mainmenu {
+  margin: 3px 2px 2px 1px;
+  float: left;
 }
 
 div.LC_createcourse {
@@ -8479,8 +8661,9 @@ fieldset {
 }
 
 fieldset#LC_selectuser {
-    margin: 0;
-    padding: 0;
+  margin: -1px 0 0 0;
+  padding: 0;
+  border: 0;
 }
 
 article.geogebraweb div {
@@ -9038,7 +9221,7 @@ ul#LC_toolbar {
   padding: 0;
   margin: 2px;
   list-style:none;
-  position:relative;
+  display:inline;
   background-color:white;
   overflow: auto;
 }
@@ -9066,6 +9249,13 @@ a.LC_toolbarItem {
   background-color:transparent;
 }
 
+.LC_navtools {
+  display: inline-block;
+  padding: 0;
+  margin: 2px;
+  vertical-align: middle;
+}
+
 ul.LC_funclist {
     margin: 0;
     padding: 0.5em 1em 0.5em 0;
@@ -9170,13 +9360,39 @@ pre.LC_wordwrap {
 /*
   styles used for response display
 */
-div.LC_radiofoil, div.LC_rankfoil {
+div.LC_radiofoil, div.LC_rankfoil, div.LC_optionfoil, div.LC_matchfoil, div.LC_login_links {
   margin: .5em 0em .5em 0em;
 }
 table.LC_itemgroup {
   margin-top: 1em;
 }
 
+table.LC_itemgroup tr th {
+  font-weight: normal;
+}
+
+fieldset.LC_webbubbles {
+  margin: 2px 0 0 0;
+  padding: 0;
+  border: 0;
+}
+
+ul.LC_webbubbles {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+  text-align: left;
+  float: left;
+}
+
+ul.LC_webbubbles li {
+  line-height: 1.8em;
+  border: 1px solid black;
+  padding: 0 2px 0 5px;
+  margin: 0 0 0 -1px;
+  float: left;
+}
+
 /*
   styles used by TTH when "Default set of options to pass to tth/m
   when converting TeX" in course settings has been set
@@ -9372,7 +9588,7 @@ sub headtag {
         $inhibitprint = &print_suppression();
     }
 
-    if (!$args->{'frameset'}) {
+    if (!$args->{'frameset'} && !$args->{'switchserver'}) {
 	$result .= &Apache::lonhtmlcommon::htmlareaheaders();
     }
     if ($args->{'force_register'} && $env{'request.noversionuri'} !~ m{^/res/adm/pages/}) {
@@ -9380,7 +9596,8 @@ sub headtag {
     }
     if (!$args->{'no_nav_bar'} 
 	&& !$args->{'only_body'}
-	&& !$args->{'frameset'}) {
+	&& !$args->{'frameset'}
+	&& !$args->{'switchserver'}) {
 	$result .= &help_menu_js($httphost);
         $result.=&modal_window();
         $result.=&togglebox_script();
@@ -9576,8 +9793,12 @@ OFFLOAD
 	$title = 'The LearningOnline Network with CAPA';
     }
     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
-    $result .= '<title> LON-CAPA '.$title.'</title>'
-	.'<link rel="stylesheet" type="text/css" href="'.$url.'"';
+    if ($title =~ /^LON-CAPA\s+/) {
+        $result .= '<title> '.$title.'</title>';
+    } else {
+        $result .= '<title> LON-CAPA '.$title.'</title>';
+    }
+    $result .= "\n".'<link rel="stylesheet" type="text/css" href="'.$url.'"';
     if (!$args->{'frameset'}) {
         $result .= ' /';
     }
@@ -9592,10 +9813,14 @@ OFFLOAD
     }
     if ($clientmobile) {
         $result .= '
-<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta name="apple-mobile-web-app-capable" content="yes" />';
     }
-    $result .= '<meta name="google" content="notranslate" />'."\n";
+    $result .= '<meta name="google" content="notranslate"';
+    if (!$args->{'frameset'}) {
+        $result .= ' /';
+    }
+    $result .= '>'."\n";
     return $result.'</head>';
 }
 
@@ -9726,7 +9951,7 @@ sub xml_begin {
 	    .'xmlns="http://www.w3.org/1999/xhtml">';
     } elsif ($is_frameset) {
         $output='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'."\n".
-                '<html>'."\n";
+                '<html lang="en">'."\n";
     } else {
 	$output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n".
                 '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
@@ -9776,6 +10001,11 @@ $args - additional optional args support
              no_auto_mt_title -> prevent &mt()ing the title arg
              bread_crumbs ->             Array containing breadcrumbs
              bread_crumbs_component ->  if exists show it as headline else show only the breadcrumbs
+             bread_crumbs_style -> breadcrumbs are contained within <div id="LC_breadcrumbs">,
+                                   and &standard_css() contains CSS for #LC_breadcrumbs, if you want
+                                   to override those values, or add to them, specify the value to
+                                   include in the style attribute to include in the div tag by using
+                                   bread_crumbs_style (e.g., overflow: visible)
              bread_crumbs_nomenu -> if true will pass false as the value of $menulink
                                     to lonhtmlcommon::breadcrumbs
              group          -> includes the current group, if page is for a 
@@ -9944,9 +10174,12 @@ sub start_page {
                 }
 		#if bread_crumbs_component exists show it as headline else show only the breadcrumbs
 		if(exists($args->{'bread_crumbs_component'})){
-			$result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'},'',$menulink);
+			$result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'},
+                                                                       '',$menulink,'',
+                                                                       $args->{'bread_crumbs_style'});
                 } else {
-			$result .= &Apache::lonhtmlcommon::breadcrumbs('','',$menulink);
+			$result .= &Apache::lonhtmlcommon::breadcrumbs('','',$menulink,'',
+                                                                       $args->{'bread_crumbs_style'});
 		}
         }
     }
@@ -10631,8 +10864,10 @@ sub simple_error_page {
     }
 
     my $page =
-	&Apache::loncommon::start_page($title,'',\%displayargs).
+	&Apache::loncommon::start_page($title,'',\%displayargs)."\n".
+        '<div class="LC_landmark" style="clear:both" role="main">'.
 	'<p class="LC_error">'.$msg.'</p>'.
+        '</div>'.
 	&Apache::loncommon::end_page();
     if (ref($r)) {
 	$r->print($page);
@@ -18034,6 +18269,12 @@ sub init_user_environment {
                 } else {
                     $userenv{'editors'} = 'edit,xml';
                 }
+                if ($userenv{'authorarchive'}) {
+                    $userenv{'canarchive'} = 1;
+                } elsif (($userenv{'authorarchive'} eq '') &&
+                         ($domdef{'archive'})) {
+                    $userenv{'canarchive'} = 1;
+                }
             }
 
             $userenv{'canrequest.author'} =
@@ -18822,7 +19063,7 @@ Returns: HTML to display with informatio
 sub check_release_result {
     my ($switchwarning,$switchserver) = @_;
     my $output = &start_page('Selected course unavailable on this server').
-                 '<p class="LC_warning">';
+                 '<div class="LC_landmark" role="main"><p class="LC_warning">';
     if ($switchwarning) {
         $output .= $switchwarning.'<br /><a href="/adm/roles">';
         if (&show_course()) {
@@ -18838,7 +19079,7 @@ sub check_release_result {
                    &mt('Switch Server').
                    '</a>';
     }
-    $output .= '</p>'.&end_page();
+    $output .= '</p></div>'.&end_page();
     return $output;
 }
 
@@ -18922,8 +19163,8 @@ sub needs_coursereinit {
                     $update = 'supp';
                 }
             }
-            return ($update);
         }
+        return ($update);
     }
     return ();
 }
@@ -19454,9 +19695,9 @@ sub create_captcha {
         if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') {
             $output = '<input type="hidden" name="crypt" value="'.$md5sum.'" />'."\n".
                       '<span class="LC_nobreak">'.
-                      &mt('Type in the letters/numbers shown below').'&nbsp;'.
-                      '<input type="text" size="5" name="code" value="" autocomplete="new-password" />'.
-                      '</span><br />'.
+                      '<label>'.&mt('Type in the letters/numbers shown below').'&nbsp;'.
+                      '<input type="text" size="5" name="code" value="" autocomplete="new-password" aria-required="true" />'.
+                      '</label></span><br />'.
                       '<img src="'.$captcha_params{'www_output_dir'}.'/'.$md5sum.'.png" alt="captcha" />';
             last;
         }