--- loncom/interface/loncommon.pm	2023/11/03 01:12:15	1.1414
+++ loncom/interface/loncommon.pm	2025/01/26 07:17:02	1.1447
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.1414 2023/11/03 01:12:15 raeburn Exp $
+# $Id: loncommon.pm,v 1.1447 2025/01/26 07:17:02 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -1372,7 +1372,7 @@ sub helpLatexCheatsheet {
         $out .= '<span>'
                .&help_open_topic('Authoring_Output_Tags',&mt('Output Tags'),$stayOnPage,undef,600)
                .'</span> <span>'
-               .&help_open_topic('Authoring_Multilingual_Problems',&mt('How to create problems in different languages'),$stayOnPage,undef,600)
+               .&help_open_topic('Authoring_Multilingual_Problems',&mt('Languages'),$stayOnPage,undef,600)
 	       .'</span>';
     }
     $out .= '</span>'; # End cheatsheet
@@ -1764,8 +1764,6 @@ the id of the element to resize, second
 surrounds everything that comes after the textarea, this routine needs
 to be attached to the <body> for the onload and onresize events.
 
-=back
-
 =cut
 
 sub resize_textarea_js {
@@ -2439,6 +2437,168 @@ sub show_crsfiles_js {
 END
 }
 
+sub crsauthor_rights {
+    my ($rightsfile,$path,$docroot,$cnum,$cdom) = @_;
+    my $sourcerights = "$path/$rightsfile";
+    my $now = time;
+    if (!-e $sourcerights) {
+        my $cid = $cdom.'_'.$cnum;
+        if (!-e "$docroot/priv/$cdom") {
+            mkdir("$docroot/priv/$cdom",0755);
+        }
+        if (!-e "$docroot/priv/$cdom/$cnum") {
+            mkdir("$docroot/priv/$cdom/$cnum",0755);
+        }
+        if (open(my $fh,">$sourcerights")) {
+            print $fh <<END;
+<accessrule effect="deny" realm="" type="course" role="" />
+<accessrule effect="allow" realm="$cid" type="course" role="" />
+END
+            close($fh);
+        }
+    }
+    if (!-e "$sourcerights.meta") {
+        if (open(my $fh,">$sourcerights.meta")) {
+            my $author=$env{'environment.firstname'}.' '.
+                       $env{'environment.middlename'}.' '.
+                       $env{'environment.lastname'}.' '.
+                       $env{'environment.generation'};
+            $author =~ s/\s+$//;
+            print $fh <<"END";
+
+<abstract></abstract>
+<author>$author</author>
+<authorspace>$cnum:$cdom</authorspace>
+<copyright>private</copyright>
+<creationdate>$now</creationdate>
+<customdistributionfile></customdistributionfile>
+<dependencies></dependencies>
+<domain>$cdom</domain>
+<highestgradelevel>0</highestgradelevel>
+<keywords></keywords>
+<language>notset</language>
+<lastrevisiondate>$now</lastrevisiondate>
+<lowestgradelevel>0</lowestgradelevel>
+<mime>rights</mime>
+<modifyinguser>$env{'user.name'}:$env{'user.domain'}</modifyinguser>
+<notes></notes>
+<obsolete></obsolete>
+<obsoletereplacement></obsoletereplacement>
+<owner>$cnum:$cdom</owner>
+<rule>deny:::course,allow:$cid::course</rule>
+<sourceavail></sourceavail>
+<standards></standards>
+<subject></subject>
+<title>Course Authoring Rights</title>
+END
+            close($fh);
+        }
+    }
+    return;
+}
+
+=pod
+
+=item * &iframe_wrapper_headjs()
+
+emits javascript containing two global vars to facilitate handling of resizing
+by code in iframe_wrapper_resizejs() used when an iframe is present in a page
+with standard LON-CAPA menus.
+
+=cut
+
+#
+# Where iframe is in use, if window.onload() executes before the custom resize function
+# has been defined (jQuery), two global javascript vars (LCnotready and LCresizedef)
+# are used to ensure document.ready() triggers a call to resize, so the iframe contents
+# do not obscure the Functions menu.
+#
+
+sub iframe_wrapper_headjs {
+    return <<"ENDJS";
+<script type="text/javascript">
+// <![CDATA[
+var LCnotready = 0;
+var LCresizedef = 0;
+// ]]>
+</script>
+
+ENDJS
+
+}
+
+=pod
+
+=item * &iframe_wrapper_resizejs()
+
+emits javascript used to handle resizing for a page containing
+an iframe, to ensure that the iframe does not obscure any
+standard LON-CAPA menu items.
+
+=back
+
+=cut
+
+#
+# jQuery to use when iframe is in use and a page resize occurs.
+# This script will ensure that the iframe does not obscure any
+# standard LON-CAPA inline menus (primary, secondary, and/or
+# breadcrumbs and Functions menus. Expects javascript from
+# &iframe_wrapper_headjs() to be in head portion of the web page,
+# e.g., by inclusion in second arg passed to &start_page().
+#
+
+sub iframe_wrapper_resizejs {
+    my $offset = 5;
+    &get_unprocessed_cgi($ENV{'QUERY_STRING'},['inhibitmenu']);
+    if (($env{'form.inhibitmenu'} eq 'yes') || ($env{'form.only_body'})) {
+        $offset = 0;
+    }
+    return &Apache::lonhtmlcommon::scripttag(<<SCRIPT);
+    \$(document).ready( function() {
+        \$(window).unbind('resize').resize(function(){
+            var header = null;
+            var offset = $offset;
+            var height = 0;
+            var hdrtop = 0;
+            if (\$('div.LC_menus_content:first').length) {
+                if (\$('div.LC_menus_content:first').hasClass ("shown")) {
+                    header = \$('div.LC_menus_content:first');
+                    offset = 12;
+                }
+            } else if (\$('div.LC_head_subbox:first').length) {
+                header = \$('div.LC_head_subbox:first');
+                offset = 9;
+            } else {
+                if (\$('#LC_breadcrumbs').length) {
+                    header = \$('#LC_breadcrumbs');
+                }
+            }
+            if (header != null && header.length) {
+                height = header.height();
+                hdrtop = header.position().top;
+            }
+            var pos = height + hdrtop + offset;
+            \$('.LC_iframecontainer').css('top', pos);
+        });
+        LCresizedef = 1;
+        if (LCnotready == 1) {
+            LCnotready = 0;
+            \$(window).trigger('resize');
+        }
+    });
+    window.onload = function(){
+         if (LCresizedef) {
+             LCnotready = 0;
+             \$(window).trigger('resize');
+         } else {
+             LCnotready = 1;
+         }
+    };
+SCRIPT
+
+}
+
 =pod
 
 =head1 Excel and CSV file utility routines
@@ -3790,6 +3950,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';
@@ -5516,7 +5691,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;
                     }
@@ -5607,8 +5783,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'}) {
@@ -5943,6 +6119,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') {
@@ -6376,7 +6554,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
@@ -6461,6 +6639,147 @@ sub CSTR_pageheader {
     return $output;
 }
 
+##############################################
+=pod
+
+=item * &nocodemirror()
+
+Input: None
+
+Returns: 1 if CodeMirror is deactivated based on
+         user's preference, or domain default,
+         if user indicated use of default.
+
+=cut
+
+sub nocodemirror {
+    my $nocodem = $env{'environment.nocodemirror'};
+    unless ($nocodem) {
+        my %domdefs = &Apache::lonnet::get_domain_defaults($env{'user.domain'});
+        if ($domdefs{'nocodemirror'}) {
+            $nocodem = 'yes';
+        }
+    }
+    if ($nocodem eq 'yes') {
+        return 1;
+    }
+    return;
+}
+
+##############################################
+=pod
+
+=item * &permitted_editors()
+
+Input: $uri (optional)
+
+Returns: %editors hash in which keys are editors
+         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.
+
+         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,$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)}) {
+        ($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;
+            }
+        }
+    } elsif ($env{'request.course.id'}) {
+        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);
+        }
+        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;
+                }
+            }
+        }
+    }
+    if ($is_author) {
+        if (exists($env{'environment.editors'})) {
+            map { $editors{$_} = 1; } split(/,/,$env{'environment.editors'});
+        } else {
+            %editors = ( edit => 1,
+                         xml => 1,
+                       );
+        }
+    } elsif ($is_coauthor) {
+        if (exists($env{"environment.internal.editors./$audom/$auname"})) {
+            map { $editors{$_} = 1; } split(/,/,$env{"environment.internal.editors./$audom/$auname"});
+        } else {
+            %editors = ( edit => 1,
+                         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,
+                   );
+    }
+    return %editors;
+}
+
 ###############################################
 ###############################################
 
@@ -6561,7 +6880,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);
 
@@ -6684,13 +7002,40 @@ 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 '') {
+            $collapsible = 1;
+            my ($menustate,$tiptext,$divclass);
+            if ($args->{'start_collapsed'}) {
+                $menustate = 'collapsed';
+                $tiptext = 'display';
+                $divclass = 'hidden';
+            } else {
+                $menustate = 'expanded';
+                $tiptext = 'hide';
+                $divclass = 'shown';
+            }
+            my $alttext = &mt('menu state: '.$menustate);
+            my $tooltip = &mt($tiptext.' standard menus');
+            $bodytag .= <<"END";
+<div id="LC_expandingContainer" style="display:inline;">
+<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">
+END
+        }
         unless ($args->{'no_primary_menu'}) {
             my ($left,$right) = Apache::lonmenu::primary_menu($crstype,$ltimenu,$menucoll,$menuref,
                                                               $args->{'links_disabled'},
-                                                              $args->{'links_target'});
+                                                              $args->{'links_target'},
+                                                              $collapsible);
 
             if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
                 if ($dc_info) {
@@ -6698,6 +7043,9 @@ sub bodytag {
                 }
                 $bodytag .= qq|<div id="LC_nav_bar">$left $role<br />
                                <em>$realm</em> $dc_info</div>|;
+                if ($need_endlcint) {
+                    $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
+                }
                 return $bodytag;
             }
 
@@ -6715,6 +7063,9 @@ sub bodytag {
 
         #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
@@ -6727,7 +7078,9 @@ sub bodytag {
                                                             $args->{'links_target'});
             }
             $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,
@@ -6743,13 +7096,19 @@ sub bodytag {
                                                         $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.
+        } 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" />';
-            $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); 
+            if ($need_endlcint) {
+                $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
+            }
+        }
+        if ($args->{'collapsible_header'} ne '') {
+            $bodytag .= $args->{'collapsible_header'}.
+                        '<div id="LC_collapsible_separator"></div>'.
+                        '</div></div>';
         }
-
         return $bodytag;
 }
 
@@ -6878,7 +7237,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);
@@ -6931,6 +7289,7 @@ body {
   line-height:130%;
   font-size:0.83em;
   color:$font;
+  background-color: $pgbg_or_bgcolor;
 }
 
 a:focus,
@@ -6942,6 +7301,24 @@ form, .inline {
   display: inline;
 }
 
+.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_menus_content.shown{
+  display: block;
+}
+
+.LC_menus_content.hidden {
+  display: none;
+}
+
 .LC_right {
   text-align:right;
 }
@@ -6962,6 +7339,12 @@ form, .inline {
   width:400px;
 }
 
+#LC_collapsible_separator {
+    border: 1px solid black;
+    width: 99.9%;
+    height: 0px;
+}
+
 .LC_iframecontainer {
     width: 98%;
     margin: 0;
@@ -7844,6 +8227,14 @@ 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;
+}
+
 span.LC_prior_numerical,
 span.LC_prior_string,
 span.LC_prior_custom,
@@ -9068,7 +9459,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/}) {
@@ -9076,7 +9467,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();
@@ -9272,8 +9664,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 .= ' /';
     }
@@ -9288,7 +9684,7 @@ 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";
@@ -9472,6 +9868,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 
@@ -9640,9 +10041,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'});
 		}
         }
     }
@@ -17563,7 +17967,8 @@ sub init_user_environment {
 
     my $public=($username eq 'public' && $domain eq 'public');
 
-    my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv);
+    my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv,
+        $coauthorenv);
     my $now=time;
 
     if ($public) {
@@ -17720,6 +18125,23 @@ sub init_user_environment {
                                                       \%userenv,\%domdef,\%is_adv);
             }
 
+            if ((ref($userroles) eq 'HASH') && ($userroles->{'user.author'}) &&
+                (exists($userroles->{"user.role.au./$domain/"}))) {
+                if ($userenv{'authoreditors'}) {
+                    $userenv{'editors'} = $userenv{'authoreditors'};
+                } elsif ($domdef{'editors'} ne '') {
+                    $userenv{'editors'} = $domdef{'editors'};
+                } else {
+                    $userenv{'editors'} = 'edit,xml';
+                }
+                if ($userenv{'authorarchive'}) {
+                    $userenv{'canarchive'} = 1;
+                } elsif (($userenv{'authorarchive'} eq '') &&
+                         ($domdef{'archive'})) {
+                    $userenv{'canarchive'} = 1;
+                }
+            }
+
             $userenv{'canrequest.author'} =
                 &Apache::lonnet::usertools_access($username,$domain,'requestauthor',
                                                   'reload','requestauthor',