--- loncom/interface/lonmenu.pm	2009/12/16 14:21:59	1.310
+++ loncom/interface/lonmenu.pm	2011/10/03 14:30:03	1.315.2.13
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Routines to control the menu
 #
-# $Id: lonmenu.pm,v 1.310 2009/12/16 14:21:59 wenzelju Exp $
+# $Id: lonmenu.pm,v 1.315.2.13 2011/10/03 14:30:03 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -42,17 +42,72 @@ Coordinates the response to clicking an
 This is part of the LearningOnline Network with CAPA project
 described at http://www.lon-capa.org.
 
+=head1 GLOBAL VARIABLES
+
+=over
+
+=item @desklines
+
+Each element of this array contains a line of mydesk.tab that doesn't start with
+cat, prim or scnd. 
+It gets filled in the BEGIN block of this module.
+
+=item %category_names
+
+The keys of this hash are the abbreviations used in mydesk.tab in those lines that 
+start with cat, the values are strings representing titles. 
+It gets filled in the BEGIN block of this module.
+
+=item %category_members
+
+TODO 
+
+=item %category_positions
+
+The keys of this hash are the abbreviations used in mydesk.tab in those lines that
+start with cat, its values are position vectors (column, row). 
+It gets filled in the BEGIN block of this module.
+
+=item $readdesk
+
+Indicates that mydesk.tab has been read. 
+It is set to 'done' in the BEGIN block of this module.
+
+=item @primary_menu
+
+The elements of this array reference arrays that are made up of the components
+of those lines of mydesk.tab that start with prim.
+It is used by primary_menu() to generate the corresponding menu.
+It gets filled in the BEGIN block of this module.
+
+=item @secondary_menu
+
+The elements of this array reference arrays that are made up of the components
+of those lines of mydesk.tab that start with scnd.
+It is used by secondary_menu() to generate the corresponding menu.
+It gets filled in the BEGIN block of this module.
+
+=back
+
 =head1 SUBROUTINES
 
 =over
 
-Little texts
+=item prep_menuitems(\@menuitem)
+
+This routine wraps a menuitem in proper HTML. It is used by primary_menu() and 
+secondary_menu().
+
+=item primary_menu()
 
-=item initlittle()
+This routine evaluates @primary_menu and returns XHTML for the menu
+that contains following links: About, Message, Roles, Help, Logout
+@primary_menu is filled within the BEGIN block of this module with 
+entries from mydesk.tab 
 
-=item menubuttons()
+=item secondary_menu()
 
-This gets called at the top of the body section
+Same as primary_menu() but operates on @secondary_menu.
 
 =item show_return_link()
 
@@ -150,10 +205,10 @@ sub prep_menuitem {
     } else {             # textual Link
         $link = &mt($$menuitem[3]);
     }
-    if($$menuitem[4] eq 'newmsg'){   #special style for New Messages
-        return '<li><a href="'.$$menuitem[0].'"><span class="LC_new_message">'.$link.'</span></a></li>';
-    }
-    return '<li><a href="'.$$menuitem[0].'">'.$link.'</a></li>';
+    return '<li><a'
+           # highlighting for new messages
+           . ( $$menuitem[4] eq 'newmsg' ? ' class="LC_new_message"' : '')
+           . qq| href="$$menuitem[0]" target="_top">$link</a></li>|;
 }
 
 # primary_menu() evaluates @primary_menu and returns XHTML for the menu
@@ -165,6 +220,11 @@ sub primary_menu {
     my $menu;
     # each element of @primary contains following array:
     # (link url, icon path, alt text, link text, condition)
+    my $public;
+    if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public'))
+        || (($env{'user.name'} eq '') && ($env{'user.domain'} eq ''))) {
+        $public = 1;
+    }
     foreach my $menuitem (@primary_menu) {
         # evaluate conditions 
         next if    ref($menuitem)       ne 'ARRAY';    #
@@ -172,39 +232,76 @@ sub primary_menu {
                 && &Apache::lonmsg::mynewmail();       # whether a new msg 
         next if    $$menuitem[4]        eq 'newmsg'    # arrived or not
                 && !&Apache::lonmsg::mynewmail();      # 
-        next if    $$menuitem[4]        !~ /public/    ##we've a public user, 
-                && $env{'user.name'}    eq 'public'    ##who should not see all 
-                && $env{'user.domain'}  eq 'public';   ##links
-        next if    $$menuitem[4]        eq 'onlypublic'# hide links which are 
-                && $env{'user.name'}    ne 'public'    # only visible to public
-                && $env{'user.domain'}  ne 'public';   # users
+        next if    $$menuitem[4]        !~ /public/    ##we've a public user,
+                && $public;                            ##who should not see all
+                                                       ##links
+        next if    $$menuitem[4]        eq 'onlypublic'# hide links which are
+                && !$public;                           # only visible to public
+                                                       # users
         next if    $$menuitem[4]        eq 'roles'     ##show links depending on
-                && &Apache::loncommon::show_course();  ##term 'Courses' or 
+                && &Apache::loncommon::show_course();  ##term 'Courses' or
         next if    $$menuitem[4]        eq 'courses'   ##'Roles' wanted
                 && !&Apache::loncommon::show_course(); ##
-        
-            
+
+
         if ($$menuitem[3] eq 'Help') { # special treatment for helplink
-            $menu .= '<li>'.&Apache::loncommon::top_nav_help('Help').'</li>';
+            if ($public) {
+                my $origmail = $Apache::lonnet::perlvar{'lonSupportEMail'};
+                my $defdom = &Apache::lonnet::default_login_domain();
+                my $to = &Apache::loncommon::build_recipient_list(undef,
+                                                                  'helpdeskmail',
+                                                                  $defdom,$origmail);
+                if ($to ne '') {
+                    $menu .= &prep_menuitem($menuitem);
+                }
+            } else {
+                $menu .= '<li>'.&Apache::loncommon::top_nav_help('Help').'</li>';
+            }
         } else {
-            my @items = @{$menuitem};
-            $items[0] = 'javascript:'.$menuitem->[0].';';
-            $menu .= &prep_menuitem(\@items);
+            $menu .= &prep_menuitem($menuitem);
         }
     }
 
     return "<ol class=\"LC_primary_menu LC_right\">$menu</ol>";
 }
 
+#returns hashref {user=>'',dom=>''} containing:
+#   own name, domain if user is au
+#   name, domain of parent author if user is ca or aa
+#empty return if user is not an author or not on homeserver
+#
+#TODO this should probably be moved somewhere more central
+#since it can be used by different parts of the system
+sub getauthor{
+    return unless $env{'request.role'}=~/^(ca|aa|au)/; #nothing to do if user isn't some kind of author
+
+                        #co- or assistent author?
+    my ($dom, $user) = ($env{'request.role'} =~ /^(?:ca|aa)\.\/($match_domain)\/($match_username)$/)
+                       ? ($1, $2) #domain, username of the parent author
+                       : @env{ ('request.role.domain', 'user.name') }; #own domain, username
+
+    # current server == home server?
+    my $home =  &Apache::lonnet::homeserver($user,$dom);
+    foreach (&Apache::lonnet::current_machine_ids()){
+        return {user => $user, dom => $dom} if $_ eq $home;
+    }
+
+    # if wrong server
+    return;
+}
+
 
 sub secondary_menu {
     my $menu;
 
     my $crstype = &Apache::loncommon::course_type();
-    my $canedit = &Apache::lonnet::allowed('mdc', $env{'request.course.id'});
-    my $canviewgrps = &Apache::lonnet::allowed('vcg', $env{'request.course.id'}
-                   . ($env{'request.course.sec'} ? "/$env{'request.course.sec'}"
-                                                 : '')); 
+    my $crs_sec = $env{'request.course.id'} . ($env{'request.course.sec'}
+                                               ? "/$env{'request.course.sec'}"
+                                               : '');
+    my $canedit       = &Apache::lonnet::allowed('mdc', $env{'request.course.id'});
+    my $canviewgrps   = &Apache::lonnet::allowed('vcg', $crs_sec);
+    my $author        = getauthor();
+
     my $showlink = &show_return_link();
     my %groups = &Apache::lonnet::get_active_groups(
                      $env{'user.domain'}, $env{'user.name'},
@@ -214,6 +311,7 @@ sub secondary_menu {
         # evaluate conditions 
         next if    ref($menuitem)  ne 'ARRAY';
         next if    $$menuitem[4]   ne 'always'
+                && $$menuitem[4]   ne 'author'
                 && !$env{'request.course.id'};
         next if    $$menuitem[4]   eq 'showreturn'
                 && !$showlink
@@ -235,6 +333,8 @@ sub secondary_menu {
         next if    $$menuitem[4]   =~ /showgroups$/
                 && !$canviewgrps
                 && !%groups;
+        next if    $$menuitem[4]    eq 'author'
+                && !$author;
 
         if ($$menuitem[3] eq 'Roles' && $env{'request.course.id'}) {
             # special treatment for role selector
@@ -276,203 +376,18 @@ sub secondary_menu {
         $menu =~ s/\[url\]/$escurl/g;
         $menu =~ s/\[symb\]/$escsymb/g;
     }
+    $menu =~ s/\[uname\]/$$author{user}/g;
+    $menu =~ s/\[udom\]/$$author{dom}/g;
 
     return "<ul id=\"LC_secondary_menu\">$menu</ul>";
 }
 
+sub show_return_link {
+    if (($env{'request.noversionuri'} =~ m{^/adm/(viewclasslist|navmaps)($|\?)})
+        || ($env{'request.noversionuri'} =~ m{^/adm/.*/aboutme($|\?)})) {
 
-#
-# This routine returns a translated hash for the menu items in the top inline menu row
-# Probably should be in mydesk.tab
-
-#SD this sub is deprecated - don't use it
-sub initlittle {
-    return &Apache::lonlocal::texthash('ret' => 'Return to Last Location',
-				       'nav' => 'Course Contents',
-				       'main' => 'Main Menu',
-                                       'roles' => (&Apache::loncommon::show_course()?
-                                                    'Courses':'Roles'),
-                                       'other' => 'Other Roles',
-                                       'docs' => 'Edit Course',
-                                       'exit' => 'Logout',
-                                       'login' => 'Log In',
-				       'launch' => 'Launch Remote Control',
-                                       'groups' => 'Groups',
-                                       'gdoc' => 'Community Documents',
-                                       );
-}
-
-#SD this sub is deprecated - don't use it
-#SD functionality is covered by new loncommon::bodytag and primary_menu(), secondary_menu()
-sub menubuttons {
-    my $forcereg=shift;
-    my $titletable=shift;
-#
-# Early-out for pages that should not have a menu, triggered by query string "inhibitmenu=yes"
-#
-    &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
-					    ['inhibitmenu']);
-    if (($env{'form.inhibitmenu'} eq 'yes') ||
-        ($ENV{'REQUEST_URI'} eq '/adm/logout')) { return ''; }
-
-    if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) { return ''; }
-
-    my %lt=&initlittle();
-    my $navmaps='';
-    my $reloadlink='';
-    my $docs='';
-    my $groups='';
-    my $roles='<a href="/adm/roles" target="_top">'.$lt{'roles'}.'</a>';
-    my $role_selector;
-    my $showgroups=0;
-    my ($cnum,$cdom);
-#
-# if the URL is hidden, symbs and the non-versioned version of the URL would be encrypted
-#
-    my $escurl=&escape(&Apache::lonenc::check_encrypt($env{'request.noversionuri'}));
-    my $escsymb=&escape(&Apache::lonenc::check_encrypt($env{'request.symb'}));
-
-    my $logo=&Apache::loncommon::lonhttpdurl("/adm/lonIcons/minilogo.gif");
-    $logo = '<a href="/adm/about.html"><img src="'.
-	$logo.'" alt="LON-CAPA Logo" class="LC_noBorder" /></a>';
-
-    if ($env{'request.state'} eq 'construct') {
-#
-# We are in construction space
-#
-        if (($env{'request.noversionuri'} eq '') || (!defined($env{'request.noversionuri'}))) {
-            my $returnurl = $env{'request.filename'};
-            $returnurl =~ s:^/home/([^/]+)/public_html/(.*)$:/priv/$1/$2:;
-            $escurl = &escape($returnurl);
-        }
-    }
-    if ($env{'request.course.id'}) {
-#
-# We are in a course
-#
-        $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
-        $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
-        my %coursegroups;
-        my $viewgrps_permission =
-	    &Apache::lonnet::allowed('vcg',$env{'request.course.id'}.($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:''));
-        if (!$viewgrps_permission) {
-            %coursegroups = &Apache::lonnet::get_active_groups($env{'user.domain'},$env{'user.name'},$cdom,$cnum);
-	}
-        if ((keys(%coursegroups) > 0) || ($viewgrps_permission)) {
-            $showgroups = 1;
-        }
-        $role_selector = &roles_selector($cdom,$cnum);
-        if ($role_selector) {
-            $roles = '<span class="LC_nobreak">'.$role_selector.'&nbsp;&nbsp;<a href="/adm/roles" target="_top">'.$lt{'other'}.'</a></span>';
-        }
-    }
-
-    if ($env{'environment.remote'} eq 'off') {
-# Remote Control is switched off
-# figure out colors
-        my %lt=&initlittle();
-
-        my $domain=&Apache::loncommon::determinedomain();
-        my $function=&Apache::loncommon::get_users_function();
-        my $link=&Apache::loncommon::designparm($function.'.link',$domain);
-        my $alink=&Apache::loncommon::designparm($function.'.alink',$domain);
-        my $vlink=&Apache::loncommon::designparm($function.'.vlink',$domain);
-        my $sidebg=&Apache::loncommon::designparm($function.'.sidebg',$domain);
-
-        if ($env{'user.name'} eq 'public' && $env{'user.domain'} eq 'public') {
-            return (<<ENDINLINEMENU);
-            <ol class="LC_primary_menu LC_right">
-                <li>$logo</li>
-                <li><a href="/adm/roles" target="_top">$lt{'login'}</a></li>
-            </ol>
-            <hr />
-ENDINLINEMENU
-        }
-        $roles = '<a href="/adm/roles" target="_top">'.$lt{'roles'}.'</a>';
-# Do we have a NAV link?
-        if ($env{'request.course.id'}) {
-	    my $link='/adm/navmaps?postdata='.$escurl.'&amp;postsymb='.
-		$escsymb;
-	    if ($env{'environment.remotenavmap'} eq 'on') {
-		$link="javascript:gonav('".$link."')";
-	    }
-	    $navmaps=(<<ENDNAV);
-<li><a href="$link" target="_top">$lt{'nav'}</a></li>
-ENDNAV
-            my $is_community = 
-                (&Apache::loncommon::course_type() eq 'Community');
-	    if (&Apache::lonnet::allowed('mdc',$env{'request.course.id'})) {
-                my $text = ($is_community) ? $lt{'gdoc'} : $lt{'docs'};
-		$docs=(<<ENDDOCS);
-<li><a href="/adm/coursedocs" target="_top">$text</a></li>
-ENDDOCS
-            }
-            if ($showgroups) {
-                $groups =(<<ENDGROUPS);
-<li><a href="/adm/coursegroups" target="_top">$lt{'groups'}</a></li>
-ENDGROUPS
-            }
-	    if (&show_return_link()) {
-                my $escreload=&escape('return:');
-                $reloadlink=(<<ENDRELOAD);
-<li><a href="/adm/flip?postdata=$escreload" target="_top">$lt{'ret'}</a></li>
-ENDRELOAD
-            }
-            if ($role_selector) {
-            	#$roles = '<td>'.$role_selector.'</td><td><a href="/adm/roles" target="_top">'.$lt{'other'}.'</a></td>';
-				$role_selector = '<li>'.$role_selector.'</li>';
-            }
-        }
-	if (($env{'request.state'} eq 'construct') && ($env{'request.course.id'})) {
-	    my $escreload=&escape('return:');
-	    $reloadlink=(<<ENDCRELOAD);
-<li><a href="/adm/flip?postdata=$escreload" target="_top">$lt{'ret'}</a></li>
-ENDCRELOAD
-        }
-    my $reg     = $forcereg ? &innerregister($forcereg,$titletable) : '';
-    my $form    = &serverform();
-    my $utility = &utilityfunctions();
-
-    #Prepare the message link that indicates the arrival of new mail
-    my $messagelink = &Apache::lonmsg::mynewmail() ? "Message (new)" : "Message";
-       $messagelink = '<a href="javascript:go(\'/adm/communicate\');">'
-                      . mt($messagelink) .'</a>';
-
-    my $helplink = &Apache::loncommon::top_nav_help('Help');
-	return (<<ENDINLINEMENU);
-<script type="text/javascript">
-// <![CDATA[
-// BEGIN LON-CAPA Internal
-$utility
-// ]]>
-</script>
-<ol class="LC_primary_menu LC_right">
-	<li>$logo</li>
-	<li>$messagelink</li>
-	<li>$roles</li>
-	<li>$helplink</li>
-	<li><a href="/adm/logout" target="_top">$lt{'exit'}</a></li>
-</ol>
-<ul id="LC_secondary_menu">
-<li><a href="/adm/menu" target="_top">$lt{'main'}</a></li>
-$reloadlink
-$navmaps
-$docs
-$groups
-$role_selector
-</ul>
-$form
-<script type="text/javascript">
-// END LON-CAPA Internal
-</script>
-$reg
-ENDINLINEMENU
-    } else {
-	return '';
+        return if ($env{'form.register'});
     }
-}
-
-sub show_return_link {
     return (($env{'request.noversionuri'}=~m{^/(res|public)/} &&
 	     $env{'request.symb'} eq '')
 	    ||
@@ -481,7 +396,7 @@ sub show_return_link {
 	    (($env{'request.noversionuri'}=~/^\/adm\//) &&
 	     ($env{'request.noversionuri'}!~/^\/adm\/wrapper\//) &&
 	     ($env{'request.noversionuri'}!~
-	      m[^/adm/.*/(smppg|bulletinboard|aboutme)($|\?)])
+	      m[^/adm/.*/(smppg|bulletinboard)($|\?)])
 	     ));
 }
 
@@ -538,12 +453,12 @@ sub innerregister {
         $newmail= 'swmenu.setstatus("you have","messages");';
     } 
 
-    my ($breadcrumb,$separator);
+    my ($breadcrumb,$separator,$resurl);
     if ($noremote
 	     && ($env{'request.symb'}) 
 	     && ($env{'request.course.id'})) {
 
-        my ($mapurl,$rid,$resurl) = &Apache::lonnet::decode_symb(&Apache::lonnet::symbread());
+        (my $mapurl, my $rid,$resurl) = &Apache::lonnet::decode_symb(&Apache::lonnet::symbread());
         my $coursetitle = $env{'course.'.$env{'request.course.id'}.'.description'};
 
         my $maptitle = &Apache::lonnet::gettitle($mapurl);
@@ -554,9 +469,16 @@ sub innerregister {
         } else {
             $contentstext = &mt('Course Contents');
         }
-        my @crumbs = ({text  => $contentstext, 
-                       href  => "Javascript:gonav('/adm/navmaps')"});
-
+        my @crumbs;
+        unless (($forcereg) && ($env{'request.noversionuri'} eq '/adm/navmaps')
+                && ($mapurl eq $env{'course.'.$env{'request.course.id'}.'.url'})) {
+            my $navhref = "javascript:gopost('/adm/navmaps','')";
+            if ($env{'environment.remotenavmap'} eq 'on') {
+                 $navhref = "javascript:gonav('/adm/navmaps');";
+            }
+            @crumbs = ({text  => $contentstext,
+                        href  => $navhref});
+        }
         if ($mapurl ne $env{'course.'.$env{'request.course.id'}.'.url'}) { 
             push(@crumbs, {text  => '...',
                            no_mt => 1});
@@ -570,6 +492,7 @@ sub innerregister {
 
         &Apache::lonhtmlcommon::clear_breadcrumbs();
         &Apache::lonhtmlcommon::add_breadcrumb(@crumbs);
+
         #$breadcrumb .= &Apache::lonhtmlcommon::breadcrumbs(undef,undef,0);
 	unless (($env{'request.state'} eq 'edit') || ($newmail) ||
 		($env{'request.state'} eq 'construct') ||
@@ -577,6 +500,11 @@ sub innerregister {
             $separator = &Apache::loncommon::head_subbox();
         }
         #
+    } elsif (!$const_space){
+        #a situation when we're looking at a resource outside of context of a
+        #course or construction space (e.g. with cumulative rights)
+        &Apache::lonhtmlcommon::clear_breadcrumbs();
+        &Apache::lonhtmlcommon::add_breadcrumb({text => 'View Resource'});
     }
     if ($env{'request.state'} eq 'construct') {
         $newmail = $titletable;
@@ -670,6 +598,8 @@ sub innerregister {
             my $cfuname='';
             my $cfudom='';
             my $uploaded;
+            my $switchserver='';
+            my $home;
             if ($env{'request.filename'}) {
                 my $file=&Apache::lonnet::declutter($env{'request.filename'});
                 if (defined($cnum) && defined($cdom)) {
@@ -680,18 +610,20 @@ sub innerregister {
                     # Check that the user has permission to edit this resource
                     ($cfuname,$cfudom)=&Apache::loncacc::constructaccess($file,$1);
                     if (defined($cfudom)) {
-		        my $home=&Apache::lonnet::homeserver($cfuname,$cfudom);
+		        $home=&Apache::lonnet::homeserver($cfuname,$cfudom);
 		        my $allowed=0;
 		        my @ids=&Apache::lonnet::current_machine_ids();
 		        foreach my $id (@ids) { if ($id eq $home) { $allowed=1; } }
 		        if ($allowed) {
                             $cfile=$file;
+                        } else {
+                            $switchserver=$file;
                         }
                     }
                 }
             }
             # Finally, turn the button on or off
-            if ($cfile && !$const_space) {
+            if (($cfile || $switchserver) && !$const_space) {
                 my $nocrsedit;
                 # Suppress display where CC has switched to student role.
                 if ($env{'request.course.id'}) {
@@ -703,9 +635,19 @@ sub innerregister {
                 if ($nocrsedit) {
                     $editbutton=&clear(6,1);
                 } else {
+                    my $bot = "go('$cfile')";
+                    if ($switchserver) {
+                        if ( $env{'request.symb'} && $env{'request.course.id'} ) {
+                            my ($mapurl,$rid,$resurl) = &Apache::lonnet::decode_symb(&Apache::lonnet::symbread());
+                            $cfile = '/adm/switchserver?otherserver='.$home.'&amp;role='.
+                                     &HTML::Entities::encode($env{'request.role'},'"<>&').'&amp;symb='.
+                                     &HTML::Entities::encode($env{'request.symb'},'"<>&');
+                            $bot = "need_switchserver('$cfile');";
+                        }
+                    }
                     $editbutton=&switch
                        ('','',6,1,'pcstr.gif','edit[_1]','resource[_2]',
-                     "go('".$cfile."');","Edit this resource");
+                       $bot,"Edit this resource");
                     $noeditbutton = 0;
                 }
             } elsif ($editbutton eq '') {
@@ -728,6 +670,18 @@ sub innerregister {
                 }
             }
         }
+        if ($env{'request.course.id'}) {
+            if ($resurl eq "public/$cdom/$cnum/syllabus") {
+                if ($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'} =~ /\w/) {
+                    if (&Apache::lonnet::allowed('mdc',$env{'request.course.id'})) {
+                        $editbutton=&switch('','',6,1,'pcstr.png','Edit',
+                                            'resource[_2]',
+                                            "go('/adm/courseprefs?phase=display&actions=courseinfo')",
+                                            'Edit this resource');
+                    }
+                }
+            }
+        }
         ###
         ###
 # Prepare the rest of the buttons
@@ -770,6 +724,18 @@ ENDMENUITEMS
 # We are in a course and looking at a registred URL
 # Should probably be in mydesk.tab
 #
+
+            my %icon_text;
+            if ($noremote) {
+                %icon_text = &Apache::lonlocal::texthash (
+                               annotate => 'Notes',
+                               bookmark => 'Bookmark',
+                               catalog  => 'Info',
+                               evaluate => 'Evaluate',
+                               feedback => 'Communicate',
+                               printout => 'Print',
+                             );
+            }
 	    $menuitems=(<<ENDMENUITEMS);
 c&3&1
 s&2&1&back.gif&backward[_1]&&gopost('/adm/flip','back:'+currentURL)&Go to the previous resource in the course sequence&&1
@@ -777,8 +743,8 @@ s&2&3&forw.gif&forward[_1]&&gopost('/adm
 c&6&3
 c&8&1
 c&8&2
-s&8&3&prt.gif&prepare[_1]&printout[_1]&gopost('/adm/printout',currentURL)&Prepare a printable document
-s&9&1&sbkm.gif&set[_1]&bookmark[_2]&set_bookmark()&Set a bookmark for this resource&&1
+s&8&3&prt.gif&prepare[_1]&printout[_1]&gopost('/adm/printout',currentURL)&Prepare a printable document&&&$icon_text{'printout'}
+s&9&1&sbkm.gif&set[_1]&bookmark[_2]&set_bookmark()&Set a bookmark for this resource&&1&$icon_text{'bookmark'}
 ENDMENUITEMS
 
 my $currentURL = &Apache::loncommon::get_symb();
@@ -791,7 +757,7 @@ if(length($annotation) > 0){
 	$menuitems.="anot.gif";
 }
 $menuitems.="&anno-[_1]&tations[_1]&annotate()&";
-$menuitems.="Make notes and annotations about this resource&&1\n";
+$menuitems.="Make notes and annotations about this resource&&1&$icon_text{'annotate'}\n";
 
             unless ($noremote) { 
                 my $showreqcrs = &check_for_rcrs();
@@ -800,21 +766,26 @@ $menuitems.="Make notes and annotations
                                 "&go('/adm/requestcourse')&Course requests\n";
                 }
             }
-            unless ($env{'request.noversionuri'}=~/\/(bulletinboard|smppg|navmaps|syllabus|aboutme)(\?|$)/) {
+            unless ($env{'request.noversionuri'}=~/\/(bulletinboard|smppg|navmaps|syllabus|aboutme|viewclasslist|portfolio)(\?|$)/) {
 		if ((!$env{'request.enc'}) && ($env{'request.noversionuri'} !~ m{^/adm/wrapper/ext/})) {
+                    my $tail;
+                    unless ($env{'request.state'} eq 'construct') {
+                        $tail = '&&&'.$icon_text{'catalog'};
+                    }
 		    $menuitems.=(<<ENDREALRES);
-s&6&3&catalog.gif&catalog[_2]&info[_1]&catalog_info()&Show Metadata
+s&6&3&catalog.gif&catalog[_2]&info[_1]&catalog_info()&Show Metadata$tail
 ENDREALRES
                 }
 	        $menuitems.=(<<ENDREALRES);
-s&8&1&eval.gif&evaluate[_1]&this[_1]&gopost('/adm/evaluate',currentURL,1)&Provide my evaluation of this resource
-s&8&2&fdbk.gif&feedback[_1]&discuss[_1]&gopost('/adm/feedback',currentURL,1)&Provide feedback messages or contribute to the course discussion about this resource
+s&8&1&eval.gif&evaluate[_1]&this[_1]&gopost('/adm/evaluate',currentURL,1)&Provide my evaluation of this resource&&&$icon_text{'evaluate'}
+s&8&2&fdbk.gif&feedback[_1]&discuss[_1]&gopost('/adm/feedback',currentURL,1)&Provide feedback messages or contribute to the course discussion about this resource&&&$icon_text{'feedback'}
 ENDREALRES
 	    }
         }
 	if ($env{'request.uri'} =~ /^\/res/) {
+            my $icontext = &mt('Print');
 	    $menuitems .= (<<ENDMENUITEMS);
-s&8&3&prt.gif&prepare[_1]&printout[_1]&gopost('/adm/printout',currentURL)&Prepare a printable document
+s&8&3&prt.gif&prepare[_1]&printout[_1]&gopost('/adm/printout',currentURL)&Prepare a printable document&&&$icontext
 ENDMENUITEMS
 	}
         my $buttons='';
@@ -842,39 +813,30 @@ ENDMENUITEMS
 	    my $inlinebuttons='';
     if ($addremote) {
 
-        #SD START (work in progress!)
         Apache::lonhtmlcommon::clear_breadcrumb_tools();
-        # Arrows for navigation
-        Apache::lonhtmlcommon::add_breadcrumb_tool( 'A', $inlineremote[21] );
-        Apache::lonhtmlcommon::add_breadcrumb_tool( 'A', $inlineremote[23] );
-        if(hidden_button_check() ne 'yes'){
-            # notes
-            Apache::lonhtmlcommon::add_breadcrumb_tool( 'B', $inlineremote[93]);
-            # bookmark
-            Apache::lonhtmlcommon::add_breadcrumb_tool( 'B', $inlineremote[91]);
-            # evaluate
-            Apache::lonhtmlcommon::add_breadcrumb_tool( 'B', $inlineremote[81]);
-            # feedback
-            Apache::lonhtmlcommon::add_breadcrumb_tool( 'B', $inlineremote[82]);
-            # print
-            Apache::lonhtmlcommon::add_breadcrumb_tool( 'B', $inlineremote[83]);
-            # metadata
-            Apache::lonhtmlcommon::add_breadcrumb_tool( 'B', $inlineremote[63]);
-
-            # ?
-            Apache::lonhtmlcommon::add_breadcrumb_tool( 'C', $inlineremote[61]);
-            # ?
-            Apache::lonhtmlcommon::add_breadcrumb_tool( 'C', $inlineremote[71]);
-            # ?
-            Apache::lonhtmlcommon::add_breadcrumb_tool( 'C', $inlineremote[72]);
-            # ?
-            Apache::lonhtmlcommon::add_breadcrumb_tool( 'C', $inlineremote[73]);
-            # ?
-            Apache::lonhtmlcommon::add_breadcrumb_tool( 'C', $inlineremote[92]);
 
+            Apache::lonhtmlcommon::add_breadcrumb_tool(
+                'navigation', @inlineremote[21,23]);
+
+        if(hidden_button_check() ne 'yes') {
+            Apache::lonhtmlcommon::add_breadcrumb_tool(
+                'tools', @inlineremote[93,91,81,82,83]);
+
+            #publish button in construction space
+            if ($env{'request.state'} eq 'construct'){
+                Apache::lonhtmlcommon::add_breadcrumb_tool(
+                     'advtools', $inlineremote[63]);
+            }else{
+                Apache::lonhtmlcommon::add_breadcrumb_tool(
+                     'tools', $inlineremote[63]);
+            }
+
+            unless ($env{'request.noversionuri'}=~ m{^/adm/(navmaps|viewclasslist)(\?|$)}) {
+                Apache::lonhtmlcommon::add_breadcrumb_tool(
+                    'advtools', @inlineremote[61,71,72,73,92]);
+            }
         }
 
-        #SD END
 #       # Registered, textual output
 #        if ( $env{'environment.icons'} eq 'iconsonly' ) {
 #            $inlinebuttons = (<<ENDARROWSINLINE);
@@ -909,7 +871,6 @@ ENDMENUITEMS
 #            }
 #        }
     }
-        #SD see below
         $breadcrumb = &Apache::lonhtmlcommon::breadcrumbs(undef,undef,0);
 	    $result =(<<ENDREGTEXT);
 <script type="text/javascript">
@@ -1250,7 +1211,7 @@ sub clear {
 # The javascript is usually similar to "go('/adm/roles')" or "cstrgo(..)".
 
 sub switch {
-    my ($uname,$udom,$row,$col,$img,$top,$bot,$act,$desc,$cat,$nobreak)=@_;
+    my ($uname,$udom,$row,$col,$img,$top,$bot,$act,$desc,$cat,$nobreak,$inlinetools)=@_;
     $act=~s/\$uname/$uname/g;
     $act=~s/\$udom/$udom/g;
     $top=&mt($top);
@@ -1260,7 +1221,9 @@ sub switch {
        $img=&mt($img);
     }
     my $idx=10*$row+$col;
-    $category_members{$cat}.=':'.$idx;
+    if ($cat ne '') {
+        $category_members{$cat}.=':'.$idx;
+    }
 
     unless ($env{'environment.remote'} eq 'off') {
        if (($row<1) || ($row>13)) { return ''; }
@@ -1304,9 +1267,13 @@ sub switch {
            if ($env{'environment.icons'} eq 'iconsonly') {
               $inlineremote[$idx]='<a title="'.$desc.'" href="javascript:'.$act.';">'.$pic.'</a>';
            } else {
+              my $icon_text = $desc;
+              if ($inlinetools) {
+                  $icon_text = $inlinetools.'&nbsp;';
+              }
 	      $inlineremote[$idx]=
 		   '<a title="'.$desc.'" class="LC_menubuttons_link" href="javascript:'.$act.';">'.$pic.
-		   '<span class="LC_menubuttons_inline_text">'.$desc.'</span></a>';
+		   '<span class="LC_menubuttons_inline_text">'.$icon_text.'&nbsp;</span></a>';
            }
        }
    }
@@ -1406,6 +1373,13 @@ sub rawconfig {
         my ($row,$col,$pro,$prt,$img,$top,$bot,$act,$desc,$cat)=split(/\:/,$line);
         $prt=~s/\$uname/$uname/g;
         $prt=~s/\$udom/$udom/g;
+        if ($env{'environment.remotenavmap'} eq 'on') {
+             unless ($env{'environment.remote'} eq 'on') {
+                 if ($img eq 'nav.gif') {
+                     $act = "gonav('/adm/navmaps','')";
+                 }
+             }
+        }
         if ($prt =~ /\$crs/) {
             next unless ($env{'request.course.id'});
             next if ($crstype eq 'Community');
@@ -1702,6 +1676,9 @@ sub utilityfunctions {
     my $end_page_bookmark = 
         &Apache::loncommon::end_page({'js_ready' => 1});
 
+    my $confirm_switch = &mt("Editing requires switching to the resource's home server.").'\n'.
+                         &mt('Switch server?');
+
 return (<<ENDUTILITY)
 
     var currentURL="$currenturl";
@@ -1719,10 +1696,13 @@ function go(url) {
    }
 }
 
-function gotop(url) {
+function need_switchserver(url) {
     if (url!='' && url!= null) {
-        top.location.href = url;
+        if (confirm("$confirm_switch")) {
+            go(url);
+        }
     }
+    return;
 }
 
 function gopost(url,postdata) {
@@ -1814,7 +1794,7 @@ function edit_bookmarks() {
    go('');
    w_BookmarkPal_flag=1;
    bookmarkpal=window.open("/adm/bookmarks",
-               "BookmarkPal", "width=400,height=505,scrollbars=0");
+               "BookmarkPal", "width=500,height=505,scrollbars=0");
 }
 
 function annotate() {
@@ -1911,7 +1891,7 @@ sub roles_selector {
     my ($cdom,$cnum) = @_;
     my $crstype = &Apache::loncommon::course_type();
     my $now = time;
-    my (%courseroles,%seccount);
+    my (%courseroles,%seccount,%courseprivs);
     my $is_cc;
     my $role_selector;
     my $ccrole;
@@ -1919,7 +1899,17 @@ sub roles_selector {
         $ccrole = 'co';
     } else {
         $ccrole = 'cc';
-    } 
+    }
+    my $priv;
+    my $destinationurl = $ENV{'REQUEST_URI'};
+    my $reqprivs = &required_privs();
+    if (ref($reqprivs) eq 'HASH') {
+        my $destination = $destinationurl;
+        $destination =~ s/(\?.*)$//;
+        if (exists($reqprivs->{$destination})) {
+            $priv = $reqprivs->{$destination};
+        }
+    }
     if ($env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum}) {
         my ($start,$end) = split(/\./,$env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum});
         
@@ -1932,7 +1922,7 @@ sub roles_selector {
         }
     }
     if ($is_cc) {
-        &get_all_courseroles($cdom,$cnum,\%courseroles,\%seccount);
+        &get_all_courseroles($cdom,$cnum,\%courseroles,\%seccount,\%courseprivs,$priv);
     } else {
         my %gotnosection;
         foreach my $item (keys(%env)) {
@@ -1948,6 +1938,18 @@ sub roles_selector {
                         $gotnosection{$role} = 1;
                     }
                 }
+                if ($priv ne '') {
+                    my $cnumsec = $cnum;
+                    if ($sec ne '') {
+                        $cnumsec .= "/$sec";
+                    }
+                    $courseprivs{"$role./$cdom/$cnumsec./"} =
+                        $env{"user.priv.$role./$cdom/$cnumsec./"};
+                    $courseprivs{"$role./$cdom/$cnumsec./$cdom/"} =
+                        $env{"user.priv.$role./$cdom/$cnumsec./$cdom/"};
+                    $courseprivs{"$role./$cdom/$cnumsec./$cdom/$cnumsec"} =
+                        $env{"user.priv.$role./$cdom/$cnumsec./$cdom/$cnumsec"};
+                }
                 if (ref($courseroles{$role}) eq 'ARRAY') {
                     if ($sec ne '') {
                         if (!grep(/^\Q$sec\E$/,@{$courseroles{$role}})) {
@@ -1973,7 +1975,7 @@ sub roles_selector {
     }
     my @roles_order = ($ccrole,'in','ta','ep','ad','st');
     if (keys(%courseroles) > 1) {
-        $role_selector = &jump_to_role($cdom,$cnum,\%seccount,\%courseroles);
+        $role_selector = &jump_to_role($cdom,$cnum,\%seccount,\%courseroles,\%courseprivs,$priv);
         $role_selector .= '<form name="rolechooser" method="post" action="/adm/roles">
                           <select name="switchrole" onchange="javascript:adhocRole('."'switchrole'".')">';
         $role_selector .= '<option value="">'.$switchtext.'</option>';
@@ -1989,7 +1991,7 @@ sub roles_selector {
         }
         $role_selector .= '</select>'."\n".
                '<input type="hidden" name="destinationurl" value="'.
-               &HTML::Entities::encode($ENV{'REQUEST_URI'}).'" />'."\n".
+               &HTML::Entities::encode($destinationurl).'" />'."\n".
                '<input type="hidden" name="gotorole" value="1" />'."\n".
                '<input type="hidden" name="selectrole" value="" />'."\n".
                '<input type="hidden" name="switch" value="1" />'."\n".
@@ -1999,18 +2001,21 @@ sub roles_selector {
 }
 
 sub get_all_courseroles {
-    my ($cdom,$cnum,$courseroles,$seccount) = @_;
-    unless ((ref($courseroles) eq 'HASH') && (ref($seccount) eq 'HASH')) {
+    my ($cdom,$cnum,$courseroles,$seccount,$courseprivs) = @_;
+    unless ((ref($courseroles) eq 'HASH') && (ref($seccount) eq 'HASH') &&
+            (ref($courseprivs) eq 'HASH')) {
         return;
     }
     my ($result,$cached) = 
         &Apache::lonnet::is_cached_new('getcourseroles',$cdom.'_'.$cnum);
     if (defined($cached)) {
         if (ref($result) eq 'HASH') {
-            if ((ref($result->{'roles'}) eq 'HASH') && 
-                (ref($result->{'seccount'}) eq 'HASH')) {
+            if ((ref($result->{'roles'}) eq 'HASH') &&
+                (ref($result->{'seccount'}) eq 'HASH') &&
+                (ref($result->{'privs'}) eq 'HASH')) {
                 %{$courseroles} = %{$result->{'roles'}};
                 %{$seccount} = %{$result->{'seccount'}};
+                %{$courseprivs} = %{$result->{'privs'}};
                 return;
             }
         }
@@ -2038,23 +2043,34 @@ sub get_all_courseroles {
                 push(@{$courseroles->{$urole}},$usec);
             }
         }
+        my $area = '/'.$cdom.'/'.$cnum;
+        if ($usec ne '') {
+            $area .= '/'.$usec;
+        }
+        if ($role =~ /^cr\//) {
+            &Apache::lonnet::custom_roleprivs($courseprivs,$urole,$cdom,$cnum,$urole.'.'.$area,$area);
+        } else {
+            &Apache::lonnet::standard_roleprivs($courseprivs,$urole,$cdom,$urole.'.'.$area,$cnum,$area);
+        }
     }
     my %sections_count = &Apache::loncommon::get_sections($cdom,$cnum,['st']);
     @{$courseroles->{'st'}} = ();
+    &Apache::lonnet::standard_roleprivs($courseprivs,'st',$cdom,"st./$cdom/$cnum",$cnum,"/$cdom/$cnum");
     if (keys(%sections_count) > 0) {
         push(@{$courseroles->{'st'}},keys(%sections_count));
-        $seccount->{'st'} = scalar(keys(%sections_count)); 
+        $seccount->{'st'} = scalar(keys(%sections_count));
     }
     my $rolehash = {
                      'roles'    => $courseroles,
                      'seccount' => $seccount,
+                     'privs'    => $courseprivs,
                    };
     &Apache::lonnet::do_cache_new('getcourseroles',$cdom.'_'.$cnum,$rolehash);
     return;
 }
 
 sub jump_to_role {
-    my ($cdom,$cnum,$seccount,$courseroles) = @_;
+    my ($cdom,$cnum,$seccount,$courseroles,$courseprivs,$priv) = @_;
     my %lt = &Apache::lonlocal::texthash(
                 this => 'This role has section(s) associated with it.',
                 ente => 'Enter a specific section.',
@@ -2062,6 +2078,8 @@ sub jump_to_role {
                 avai => 'Available sections are:',
                 youe => 'You entered an invalid section choice:',
                 plst => 'Please try again',
+                role => 'The role you selected is not permitted to view the current page.',
+                swit => 'Switch role, but display Main Menu page instead?',
     );
     my $js;
     if (ref($courseroles) eq 'HASH') {
@@ -2084,6 +2102,37 @@ sub jump_to_role {
                    '    numsec['.$i.'] = "'.$seccount->{$items[$i]}.'";'."\n";
         }
     }
+    my $checkroles = 0;
+    if ($priv && ref($courseprivs) eq 'HASH') {
+        my (%disallowed,%allowed,@disallow);
+        foreach my $role (sort(keys(%{$courseprivs}))) {
+            my $trole;
+            if ($role =~ m{^(.+?)\Q./$cdom/$cnum\E}) {
+                $trole = $1;
+            }
+            if (($trole ne '') && ($trole ne 'cm')) {
+                if ($courseprivs->{$role} =~ /\Q:$priv\E($|:|\&\w+)/) {
+                    $allowed{$trole} = 1;
+                } else {
+                    $disallowed{$trole} = 1;
+                }
+            }
+        }
+        foreach my $trole (keys(%disallowed)) {
+            unless ($allowed{$trole}) {
+                push(@disallow,$trole);
+            }
+        }
+        if (@disallow > 0) {
+            $checkroles = 1;
+            $js .= "    var disallow = new Array('".join("','",@disallow)."');\n".
+                   "    var rolecheck = 1;\n";
+        }
+    }
+    if (!$checkroles) {
+        $js .=  "    var disallow = new Array();\n".
+                "    rolecheck = 0;\n";
+    }
     return <<"END";
 <script type="text/javascript">
 //<![CDATA[
@@ -2091,7 +2140,7 @@ function adhocRole(roleitem) {
     $js
     var newrole =  document.rolechooser.elements[roleitem].options[document.rolechooser.elements[roleitem].selectedIndex].value;
     if (newrole == '') {
-        return; 
+        return;
     } 
     var fullrole = newrole+'./$cdom/$cnum';
     var selidx = '';
@@ -2100,6 +2149,18 @@ function adhocRole(roleitem) {
             selidx = i;
         }
     }
+    if (rolecheck > 0) {
+        for (var i=0; i<disallow.length; i++) {
+            if (disallow[i] == newrole) {
+                if (confirm("$lt{'role'}\\n$lt{'swit'}")) {
+                    document.rolechooser.destinationurl.value = '/adm/menu';
+                } else {
+                    document.rolechooser.elements[roleitem].selectedIndex = 0;
+                    return;
+                }
+            }
+        }
+    }
     var secok = 1;
     var secchoice = '';
     if (selidx >= 0) {
@@ -2107,7 +2168,7 @@ function adhocRole(roleitem) {
             secok = 0;
             var numrolesec = rolesections[selidx].length;
             var msgidx = numsec[selidx] - numrolesec;
-            secchoice = prompt("$lt{'this'}\\n"+secpick[msgidx]+"\\n$lt{'avai'} "+roleseclist[selidx],"");
+            secchoice = prompt("$lt{'this'} "+secpick[msgidx]+"\\n$lt{'avai'} "+roleseclist[selidx],"");
             if (secchoice == '') {
                 if (msgidx > 0) {
                     secok = 1;
@@ -2137,6 +2198,7 @@ function adhocRole(roleitem) {
         return;
     }
     if (fullrole == "$env{'request.role'}") {
+        document.rolechooser.elements[roleitem].selectedIndex = 0;
         return;
     }
     itemid = retrieveIndex('gotorole');
@@ -2162,6 +2224,22 @@ function retrieveIndex(item) {
 END
 }
 
+sub required_privs {
+    my $privs =  {
+             '/adm/parmset'      => 'opa',
+             '/adm/courseprefs'  => 'opa',
+             '/adm/whatsnew'     => 'whn',
+             '/adm/populate'     => 'cst',
+             '/adm/trackstudent' => 'vsa',
+             '/adm/statistics'   => 'vgr',
+           };
+    unless ($env{'course.'.$env{'request.course.id'}.'.grading'} eq 'spreadsheet') {
+        $privs->{'/adm/classcalc'}   => 'vgr',
+        $privs->{'/adm/assesscalc'}  => 'vgr',
+        $privs->{'/adm/studentcalc'} => 'vgr';
+    }
+    return $privs;
+}
 
 # ================================================================ Main Program