--- loncom/interface/lonmenu.pm	2016/10/16 21:49:51	1.455
+++ loncom/interface/lonmenu.pm	2018/04/14 17:52:43	1.486
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Routines to control the menu
 #
-# $Id: lonmenu.pm,v 1.455 2016/10/16 21:49:51 raeburn Exp $
+# $Id: lonmenu.pm,v 1.486 2018/04/14 17:52:43 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -99,7 +99,7 @@ It gets filled in the BEGIN block of thi
 
 =over
 
-=item prep_menuitems(\@menuitem)
+=item prep_menuitems(\@menuitem,$ltitarget)
 
 This routine wraps a menuitem in proper HTML. It is used by primary_menu() and 
 secondary_menu().
@@ -210,16 +210,17 @@ use Apache::lonenc();
 use Apache::lonlocal;
 use Apache::lonmsg();
 use LONCAPA qw(:DEFAULT :match);
+use LONCAPA::ltiutils;
 use HTML::Entities();
 use Apache::lonwishlist();
 
 use vars qw(@desklines %category_names %category_members %category_positions 
-            $readdesk @primary_menu %primary_submenu @secondary_menu);
+            $readdesk @primary_menu %primary_submenu @secondary_menu %secondary_submenu);
 
 my @inlineremote;
 
 sub prep_menuitem {
-    my ($menuitem) = @_;
+    my ($menuitem,$ltitarget) = @_;
     return '' unless(ref($menuitem) eq 'ARRAY');
     my $link;
     if ($$menuitem[1]) { # graphical Link
@@ -229,10 +230,14 @@ sub prep_menuitem {
     } else {             # textual Link
         $link = &mt($$menuitem[3]);
     }
+    my $target = ' target="_top"';
+    if ($ltitarget eq 'iframe') {
+        $target ='';
+    }
     return '<li><a' 
            # highlighting for new messages
            . ( $$menuitem[4] eq 'newmsg' ? ' class="LC_new_message"' : '') 
-           . qq| href="$$menuitem[0]" target="_top">$link</a></li>|;
+           . qq| href="$$menuitem[0]"$target>$link</a></li>|;
 }
 
 # primary_menu() evaluates @primary_menu and returns a two item array,
@@ -260,6 +265,10 @@ sub primary_menu {
         my %roles_in_env;
         $rolecount = &Apache::lonroles::roles_from_env(\%roles_in_env,$update);
     }
+    my $ltitarget;
+    if ($env{'request.lti.login'}) {
+        $ltitarget = $env{'request.lti.target'};
+    }
     foreach my $menuitem (@primary_menu) {
         # evaluate conditions 
         next if    ref($menuitem)       ne 'ARRAY';    #
@@ -293,7 +302,9 @@ sub primary_menu {
             my ($link,$target);
             if ($menuitem->[0] ne '') {
                 $link = $menuitem->[0];
-                $target = '_top';
+                unless ($ltitarget eq 'iframe') {
+                    $target = '_top';
+                }
             } else {
                 $link = '#';
             }
@@ -328,13 +339,13 @@ sub primary_menu {
                                                                   'helpdeskmail',
                                                                   $defdom,$origmail);
                 if ($to ne '') {
-                    $menu{$position} .= &prep_menuitem($menuitem); 
+                    $menu{$position} .= &prep_menuitem($menuitem,$ltitarget); 
                 }
             } else {
                 $menu{$position} .= '<li>'.&Apache::loncommon::top_nav_help('Help').'</li>';
             }
         } else {
-            $menu{$position} .= prep_menuitem($menuitem);
+            $menu{$position} .= prep_menuitem($menuitem,$ltitarget);
         }
     }
     my @output = ('','');
@@ -381,6 +392,7 @@ sub secondary_menu {
                                                ? "/$env{'request.course.sec'}"
                                                : '');
     my $canedit       = &Apache::lonnet::allowed('mdc', $env{'request.course.id'});
+    my $canvieweditor = &Apache::lonnet::allowed('cev', $env{'request.course.id'});
     my $canviewroster = $env{'course.'.$env{'request.course.id'}.'.student_classlist_view'};
     if ($canviewroster eq 'disabled') {
         undef($canviewroster);
@@ -400,10 +412,7 @@ sub secondary_menu {
     if ($env{'request.course.id'}) {
         $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
         $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
-        if ($canedit) {
-            $showsyllabus = 1;
-            $showfeeds = 1;
-        } else {
+        unless ($canedit || $canvieweditor)  {
             unless (&Apache::lonnet::is_on_map("public/$cdom/$cnum/syllabus")) {
                 if (($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'}) ||
                     ($env{'course.'.$env{'request.course.id'}.'.uploadedsyllabus'}) ||
@@ -416,7 +425,7 @@ sub secondary_menu {
                 $showfeeds = 1;
             }
         }
-        unless ($canmgr) {
+        unless ($canmgr || $canvgr) {
             my %slots = &Apache::lonnet::get_course_slots($cnum,$cdom);
             if (keys(%slots) > 0) {
                 $showresv = 1;
@@ -443,7 +452,10 @@ sub secondary_menu {
             $canmodifycoauthor = 1;
         }
     }
-    my ($roleswitcher_js,$roleswitcher_form);
+    my ($roleswitcher_js,$roleswitcher_form,$ltitarget);
+    if ($env{'request.lti.login'}) {
+        $ltitarget = $env{'request.lti.target'};
+    }
 
     foreach my $menuitem (@secondary_menu) {
         # evaluate conditions 
@@ -452,15 +464,15 @@ sub secondary_menu {
         next if    $$menuitem[4]   ne 'always'
                 && ($$menuitem[4]   ne 'author' && $$menuitem[4] ne 'cca')
                 && !$env{'request.course.id'};
-        next if    $$menuitem[4]   =~ /^mdc/
-                && !$canedit;
+        next if    $$menuitem[4]   =~ /^crsedit/
+                && (!$canedit && !$canvieweditor);
         next if    $$menuitem[4]  eq 'nvgr'
                 && $canvgr;
         next if    $$menuitem[4]  eq 'vgr'
                 && !$canvgr;
-        next if    $$menuitem[4]   eq 'cst'
+        next if    $$menuitem[4]   eq 'viewusers'
                 && !$canmodifyuser && !$canviewusers;
-        next if    $$menuitem[4]   eq 'ncst'
+        next if    $$menuitem[4]   eq 'noviewusers'
                 && ($canmodifyuser || $canviewusers || !$canviewroster);
         next if    $$menuitem[4]   eq 'mgr'
                 && !$canmgr;
@@ -470,9 +482,8 @@ sub secondary_menu {
                 && !$canviewwnew;
         next if    $$menuitem[4]   eq 'params'
                 && (!$canmodpara && !$canviewpara);
-        next if    $$menuitem[4]   =~ /showgroups$/
-                && !$canviewgrps
-                && !$grouptools;
+        next if    $$menuitem[4]   eq 'showgroups'
+                && ($canviewgrps || !$grouptools);
         next if    $$menuitem[4]   eq 'showsyllabus'
                 && !$showsyllabus;
         next if    $$menuitem[4]   eq 'showfeeds'
@@ -482,13 +493,47 @@ sub secondary_menu {
         next if    $$menuitem[4]    eq 'cca'
                 && !$canmodifycoauthor;
 
-        if ($$menuitem[3] eq 'Roles' && $env{'request.course.id'}) {
+        my $title = $menuitem->[3];
+        if (defined($secondary_submenu{$title})) {
+            my ($link,$target);
+            if ($menuitem->[0] ne '') {
+                $link = $menuitem->[0];
+                unless ($ltitarget eq 'iframe') {   
+                    $target = '_top';
+                }
+            } else {
+                $link = '#';
+            }
+            my @scndsub;
+            if (ref($secondary_submenu{$title}) eq 'ARRAY') {
+                foreach my $item (@{$secondary_submenu{$title}}) {
+                    if (ref($item) eq 'ARRAY') {
+                        next if ($item->[2] eq 'vgr' && !$canvgr);
+                        next if ($item->[2] eq 'opa' && !$canmodpara);
+                        next if ($item->[2] eq 'vpa' && !$canviewpara);
+                        next if ($item->[2] eq 'viewusers' && !($canmodifyuser || $canviewusers));
+                        next if ($item->[2] eq 'mgr' && !$canmgr);
+                        next if ($item->[2] eq 'vcg' && !$canviewgrps);
+                        next if ($item->[2] eq 'crsedit' && !$canedit && !$canvieweditor);
+                        next if ($item->[2] eq 'params' && !$canmodpara && !$canviewpara);
+                        next if ($item->[2] eq 'author' && !$author);
+                        next if ($item->[2] eq 'cca' && !$canmodifycoauthor);
+                        push(@scndsub,$item);
+                    }
+                }
+                if (@scndsub > 0) {
+                    $menu .= &create_submenu($link,$target,$title,\@scndsub,1);
+                } elsif ($link ne '#') {
+                    $menu .= '<li><a href="'.$link.'" target="'.$target.'">'.&mt($title).'</a></li>';
+                }
+            }
+        } elsif ($$menuitem[3] eq 'Roles' && $env{'request.course.id'}) {
             # special treatment for role selector
             ($roleswitcher_js,$roleswitcher_form,my $switcher) =
                 &roles_selector(
                         $env{'course.' . $env{'request.course.id'} . '.domain'},
                         $env{'course.' . $env{'request.course.id'} . '.num'},
-                        $httphost
+                        $httphost,$ltitarget
                 );
             $menu .= $switcher;
         } else {
@@ -496,14 +541,25 @@ sub secondary_menu {
                 my $url = $$menuitem[0];
                 $url =~ s{\[cdom\]/\[cnum\]}{$cdom/$cnum};
                 if (&Apache::lonnet::is_on_map($url)) {
-                    unless ($$menuitem[0] =~ /\?register=1/) {
-                        $$menuitem[0] .= '?register=1';
+                    unless ($$menuitem[0] =~ /(\?|\&)register=1/) {
+                        $$menuitem[0] .= (($$menuitem[0]=~/\?/)? '&' : '?').'register=1';
                     }
                 } else {
-                    $$menuitem[0] =~ s{\?register=1}{};
+                    $$menuitem[0] =~ s{\&?register=1}{};
+                }
+                if ($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'} =~ m{^http://}) {
+                    if (($ENV{'SERVER_PORT'} == 443) || ($env{'request.use_absolute'} =~ m{^https://})) {
+                        unless ($$menuitem[0] =~ m{^https?://}) {
+                            $$menuitem[0] = 'http://'.$ENV{'SERVER_NAME'}.$$menuitem[0];
+                        }
+                        unless ($$menuitem[0] =~ /(\&|\?)usehttp=1/) {
+                            $$menuitem[0] .= (($$menuitem[0]=~/\?/) ? '&' : '?').'usehttp=1';
+                        }
+                    }
                 }
+                $$menuitem[0] = &HTML::Entities::encode($$menuitem[0],'&<>"');
             }
-            $menu .= &prep_menuitem(\@$menuitem);
+            $menu .= &prep_menuitem(\@$menuitem,$ltitarget);
         }
     }
     if ($menu =~ /\[url\].*\[symb\]/) {
@@ -520,13 +576,13 @@ sub secondary_menu {
             my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
             ($escurl = $env{'request.filename'}) =~ s{^\Q$londocroot\E}{};
             $escurl  = &escape($escurl);
-        }    
+        }
         $menu =~ s/\[url\]/$escurl/g;
         $menu =~ s/\[symb\]/$escsymb/g;
     }
     $menu =~ s/\[uname\]/$$author{user}/g;
     $menu =~ s/\[udom\]/$$author{dom}/g;
-    if ($showsyllabus || $showfeeds) {
+    if ($env{'request.course.id'}) {
         $menu =~ s/\[cnum\]/$cnum/g;
         $menu =~ s/\[cdom\]/$cdom/g;
     }
@@ -611,7 +667,9 @@ sub build_submenu {
                     $href =~ s/\[user\]/$env{'user.name'}/g;
                 }
                 unless (($href eq '') || ($href =~ /^\#/)) {
-                    $target = ' target="_top"';
+                    if ($target eq '_top') {
+                        $target = ' target="_top"';
+                    }
                 }
 
                 $menu .= '<li style="margin:0;padding:0;'. $bordertop . $borderbot .'">';
@@ -624,7 +682,7 @@ sub build_submenu {
 }
 
 sub innerregister {
-    my ($forcereg,$bread_crumbs,$group) = @_;
+    my ($forcereg,$bread_crumbs,$group,$pagebuttonshide,$hostname) = @_;
     my $const_space = ($env{'request.state'} eq 'construct');
     my $is_const_dir = 0;
 
@@ -634,39 +692,70 @@ sub innerregister {
 
     undef(@inlineremote);
 
-    my ($mapurl,$resurl,$crstype);
+    my ($mapurl,$resurl,$crstype,$navmap,$ltiscope,$ltiuri);
 
     if ($env{'request.course.id'}) {
 #
 #course_type:  Course, Community, or Placement
 #
         $crstype = &Apache::loncommon::course_type();
+        if (($env{'request.lti.login'}) && ($env{'request.lti.uri'})) {
+            my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+            my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+            ($ltiscope,$ltiuri) = &LONCAPA::ltiutils::lti_provider_scope($env{'request.lti.uri'},$cdom,$cnum);
+        }
         if ($env{'request.symb'}) {
-            ($mapurl, my $rid, $resurl) = &Apache::lonnet::decode_symb(&Apache::lonnet::symbread());
+            my $ignorenull;
+            unless ($env{'request.noversionuri'} eq '/adm/navmaps') {
+                $ignorenull = 1;
+            }
+            my $symb = &Apache::lonnet::symbread('','',$ignorenull);
+            ($mapurl, my $rid, $resurl) = &Apache::lonnet::decode_symb($symb);
             my $coursetitle = $env{'course.'.$env{'request.course.id'}.'.description'};
 
             my $maptitle = &Apache::lonnet::gettitle($mapurl);
-            my $restitle = &Apache::lonnet::gettitle(&Apache::lonnet::symbread());
-            my @crumbs;
+            my $restitle = &Apache::lonnet::gettitle($symb);
+            my (@crumbs,@mapcrumbs);
+            if (($env{'request.noversionuri'} ne '/adm/navmaps') && ($mapurl ne '') &&
+                (!(($crstype eq 'Placement') && !$env{'request.role.adv'}))) {
+                unless ($ltiscope eq 'resource') {
+                    if (($mapurl ne $env{'course.'.$env{'request.course.id'}.'.url'}) &&
+                        !(($ltiscope eq 'map') && (&Apache::lonnet::clutter($resurl) eq $ltiuri))) {
+                        $navmap = Apache::lonnavmaps::navmap->new();
+                        if (ref($navmap)) {
+                            @mapcrumbs = $navmap->recursed_crumbs($mapurl,$restitle);
+                        }
+                    }
+                }
+            }
             unless (($forcereg) &&
                     ($env{'request.noversionuri'} eq '/adm/navmaps') &&
                     ($mapurl eq $env{'course.'.$env{'request.course.id'}.'.url'}) ||
-                    (($crstype eq 'Placement') && (!$env{'request.role.adv'}))) {
+                    (($crstype eq 'Placement') && (!$env{'request.role.adv'})) ||
+                    ($ltiscope eq 'map') || ($ltiscope eq 'resource')) {
                 @crumbs = ({text  => $crstype.' Contents', 
                             href  => "Javascript:gopost('/adm/navmaps','')"});
             }
             if ($mapurl ne $env{'course.'.$env{'request.course.id'}.'.url'}) { 
-                push(@crumbs, {text  => '...',
-                               no_mt => 1});
+                if (@mapcrumbs) {
+                    push(@crumbs,@mapcrumbs);
+                } elsif (!(($crstype eq 'Placement') && (!$env{'request.role.adv'})) &&
+                         ($ltiscope ne 'map') && ($ltiscope ne 'resource')) {
+                    push(@crumbs, {text  => '...',
+                                   no_mt => 1});
+                }
             }
 
-            unless (($crstype eq 'Placement') || (!$env{'request.role.adv'})) {
-                push @crumbs, {text => $maptitle, no_mt => 1} if ($maptitle 
-                                                           && $maptitle ne 'default.sequence' 
-                                                           && $maptitle ne $coursetitle);
+            unless ((($crstype eq 'Placement') && (!$env{'request.role.adv'})) || (@mapcrumbs) ||
+                    (!$maptitle) || ($maptitle eq 'default.sequence') ||
+                    ($mapurl eq $env{'course.'.$env{'request.course.id'}.'.url'}) ||
+                    ($ltiscope eq 'resource')) {
+                push @crumbs, {text => $maptitle, no_mt => 1, 
+                               href => &Apache::lonnet::clutter($mapurl).'?navmap=1'};
+            }
+            if ($restitle && !@mapcrumbs) {
+                push(@crumbs,{text => $restitle, no_mt => 1});
             }
-
-            push @crumbs, {text => $restitle, no_mt => 1} if $restitle; 
             my @tools;
             if ($env{'request.filename'} =~ /\.page$/) {
                 my %breadcrumb_tools = &Apache::lonhtmlcommon::current_breadcrumb_tools();
@@ -691,7 +780,7 @@ sub innerregister {
                 }
                 my $trail;
                 if ($env{'form.folderpath'}) {
-                    &prepare_functions($resurl,$forcereg,$group,undef,undef,1);
+                    &prepare_functions($resurl,$forcereg,$group,undef,undef,1,$hostname);
                     ($trail) =
                         &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1);
                 } else {
@@ -706,10 +795,10 @@ sub innerregister {
             } elsif ($resurl =~ m{^\Q/uploaded$courseurl/portfolio/syllabus/}) {
                 &Apache::lonhtmlcommon::clear_breadcrumbs();
                 &prepare_functions('/public'.$courseurl."/syllabus",
-                                   $forcereg,$group,undef,undef,1);
+                                   $forcereg,$group,undef,undef,1,$hostname);
                 $title = &mt('Syllabus File');
                 my ($trail) =
-                    &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1);
+                    &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1,$hostname);
                 return $trail;
             }
             unless ($env{'request.state'} eq 'construct') {
@@ -733,7 +822,7 @@ sub innerregister {
         $forceview,$editbutton);
     if (($resurl =~ m{^/?adm/($match_domain)/($match_username)/aboutme$}) ||
         ($env{'request.role'} !~/^(aa|ca|au)/)) {
-        $editbutton = &prepare_functions($resurl,$forcereg,$group);
+        $editbutton = &prepare_functions($resurl,$forcereg,$group,'','','',$hostname);
     }
     if ($editbutton eq '') {
         $editbutton = &clear(6,1);
@@ -746,9 +835,16 @@ sub innerregister {
         $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
         $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
         $perms{'mdc'} = &Apache::lonnet::allowed('mdc',$env{'request.course.id'});
+        $perms{'cev'} = &Apache::lonnet::allowed('cev',$env{'request.course.id'});
         my @privs;
+        my $gradable_exttool;
         if ($env{'request.symb'} ne '') {
-             if ($env{'request.filename'}=~/$LONCAPA::assess_re/) {
+             if ($env{'request.noversionuri'} =~ m{^/adm/$cdom/$cnum/(\d+)/ext\.tool$}) {
+                 if (&Apache::lonnet::EXT('resource.0.gradable') =~ /^yes$/i) {
+                     $gradable_exttool = 1;
+                     push(@privs,('mgr','vgr'));
+                 }
+             } elsif ($env{'request.filename'}=~/$LONCAPA::assess_re/) {
                  push(@privs,('mgr','vgr'));
              }
              push(@privs,('opa','vpa'));
@@ -763,8 +859,8 @@ sub innerregister {
 #
 # Determine whether or not to show Grades and Submissions buttons
 #
-        if ($env{'request.symb'} ne '' &&
-            $env{'request.filename'}=~/$LONCAPA::assess_re/) {
+        if (($env{'request.symb'} ne '') &&
+            (($env{'request.filename'}=~/$LONCAPA::assess_re/) || ($gradable_exttool))) {
             if ($perms{'mgr'}) {
                 &switch('','',7,2,'pgrd.png','Content Grades','grades[_4]',
                         "gocmd('/adm/grades','gradingmenu')",
@@ -785,7 +881,7 @@ sub innerregister {
 #
 # This applies to items inside a folder/page modifiable in the course.
 #
-        if (($env{'request.symb'}=~/^uploaded/) && ($perms{'mdc'})) {
+        if (($env{'request.symb'}=~/^uploaded/) && (($perms{'mdc'}) || ($perms{'cev'}))) {
             my $text = 'Edit Folder';
             if (($mapurl =~ /\.page$/) ||
                 ($env{'request.symb'}=~
@@ -848,7 +944,35 @@ ENDMENUITEMS
 # Should probably be in mydesk.tab
 #
             $menuitems = "c&3&1";
-            if (($crstype ne 'Placement') || ($env{'request.role.adv'})) {
+            if ($ltiscope eq 'resource') {
+# Suppress display of backward arrow for LTI Provider if scope is resource.
+# Suppress display of forward arrow for LTI Provider if scope is resource.
+            } elsif ($ltiscope eq 'map') {
+# Suppress display of backward arrow for LTI Provider if scope is map and this is first resource.
+# Suppress display of forward arrow for LTI Provider if scope is map and this is the last resource.
+                my $showforw = 1;
+                my $showback = 1;
+                my $navmap = Apache::lonnavmaps::navmap->new();
+                if (ref($navmap)) {
+                    my $mapres = $navmap->getResourceByUrl($ltiuri);
+                    if (ref($mapres)) {
+                        if ($navmap->isLastResource($mapres,$env{'request.symb'})) {
+                            $showforw = 0;
+                        }
+                        if ($navmap->isFirstResource($mapres,$env{'request.symb'})) {
+                            $showback = 0;
+                        }
+                    }
+                }
+                if ($showback) {
+                    $menuitems.="
+s&2&1&back.png&&&gopost('/adm/flip','back:'+currentURL)&Previous content resource&&1";
+                }
+                if ($showforw) {
+                    $menuitems.="
+s&2&3&forw.png&&&gopost('/adm/flip','forward:'+currentURL)&Next content resource&&3";
+                }
+            } elsif (($crstype ne 'Placement') || ($env{'request.role.adv'})) {
                 $menuitems.="
 s&2&1&back.png&&&gopost('/adm/flip','back:'+currentURL)&Previous content resource&&1
 s&2&3&forw.png&&&gopost('/adm/flip','forward:'+currentURL)&Next content resource&&3";
@@ -906,14 +1030,15 @@ if ($env{'browser.mobile'}) {
     $is_mobile = 1;
 }
 
-            unless ($env{'request.noversionuri'}=~/\/(bulletinboard|smppg|navmaps|syllabus|aboutme|viewclasslist|portfolio|exttools?)(\?|$)/) {
-		if ((!$env{'request.enc'}) && ($env{'request.noversionuri'} !~ m{^/adm/wrapper/ext/}) && ($env{'request.noversionuri'} !~ m{^/uploaded/$match_domain/$match_courseid/docs/})) {
+            unless ($env{'request.noversionuri'}=~/\/(bulletinboard|smppg|navmaps|syllabus|aboutme|viewclasslist|portfolio)(\?|$)/) {
+		if ((!$env{'request.enc'}) && ($env{'request.noversionuri'} !~ m{^/adm/wrapper/ext/}) && ($env{'request.noversionuri'} !~ m{^/uploaded/$match_domain/$match_courseid/docs/}) && ($env{'request.noversionuri'} !~ m{^/adm/.+/ext\.tool$})) {
 		    $menuitems.=(<<ENDREALRES);
 s&6&3&catalog.png&Info&info[_1]&catalog_info('$is_mobile')&Show Metadata
 ENDREALRES
                 }
                 unless (($env{'request.noversionuri'} =~ m{^/uploaded/$match_domain/$match_courseid/docs/}) ||
-                        ($env{'request.noversionuri'} =~ m{^\Q/adm/wrapper/\E(ext|uploaded)/})) {  
+                        ($env{'request.noversionuri'} =~ m{^\Q/adm/wrapper/\E(ext|uploaded)/}) ||
+                        ($env{'request.noversionuri'} =~ m{^/adm/.+/ext\.tool$})) {
                     $menuitems.=(<<ENDREALRES);
 s&8&1&eval.png&Evaluate&this[_1]&gopost('/adm/evaluate',currentURL,1)&Provide my evaluation of this resource
 ENDREALRES
@@ -969,21 +1094,23 @@ ENDMENUITEMS
 	foreach (@inlineremote) { if ($_ ne '') { $addremote=1; last;} }
 
     if ($addremote) {
-        my $countdown;
+        my ($countdown,$buttonshide);
         if ($env{'request.filename'} =~ /\.page$/) {
             my %breadcrumb_tools = &Apache::lonhtmlcommon::current_breadcrumb_tools();
             if (ref($breadcrumb_tools{'tools'}) eq 'ARRAY') {
-                $countdown = $breadcrumb_tools{'tools'}[0];
+                $countdown = $breadcrumb_tools{'tools'}->[0];
             }
+            $buttonshide = $pagebuttonshide;
         } else {
             $countdown = &countdown_timer();
+            $buttonshide = &hidden_button_check();
         }
         &Apache::lonhtmlcommon::clear_breadcrumb_tools();
 
             &Apache::lonhtmlcommon::add_breadcrumb_tool(
                 'navigation', @inlineremote[21,23]);
 
-        if (&hidden_button_check() eq 'yes') {
+        if ($buttonshide eq 'yes') {
             if ($countdown) {
                 &Apache::lonhtmlcommon::add_breadcrumb_tool('tools',$countdown);
             }
@@ -1029,12 +1156,12 @@ ENDMENUITEMS
 }
 
 sub get_editbutton {
-    my ($cfile,$home,$switchserver,$forceedit,$forceview,$forcereg) = @_;
+    my ($cfile,$home,$switchserver,$forceedit,$forceview,$forcereg,$hostname) = @_;
     my $jscall;
     if (($forceview) && ($env{'form.todocs'})) {
-        my ($folderpath,$command);
+        my ($folderpath,$command,$navmap);
         if ($env{'request.symb'}) {
-            $folderpath = &Apache::loncommon::symb_to_docspath($env{'request.symb'});
+            $folderpath = &Apache::loncommon::symb_to_docspath($env{'request.symb'},\$navmap);
         } elsif ($env{'form.folderpath'} =~ /^supplemental/) {
             $folderpath = $env{'form.folderpath'};
             $command = '&forcesupplement=1';
@@ -1042,11 +1169,16 @@ sub get_editbutton {
         $folderpath = &escape(&HTML::Entities::encode(&escape($folderpath),'<>&"'));
         $jscall = "go('/adm/coursedocs?folderpath=$folderpath$command')";
     } else {
+        my $suppanchor;
+        if ($env{'form.folderpath'}) {
+            $suppanchor = $env{'form.anchor'};
+        }
         $jscall = &Apache::lonhtmlcommon::jump_to_editres($cfile,$home,$switchserver,
                                                 $forceedit,$forcereg,$env{'request.symb'},
                                                 &escape($env{'form.folderpath'}),
-                                                &escape($env{'form.title'}),$env{'form.idx'},
-                                                &escape($env{'form.suppurl'},$env{'form.todocs'}));
+                                                &escape($env{'form.title'}),$hostname,
+                                                $env{'form.idx'},&escape($env{'form.suppurl'}),
+                                                $env{'form.todocs'},$suppanchor);
     }
     if ($jscall) {
         my $icon = 'pcstr.png';
@@ -1063,7 +1195,7 @@ sub get_editbutton {
 }
 
 sub prepare_functions {
-    my ($resurl,$forcereg,$group,$bread_crumbs,$advtools,$docscrumbs) = @_;
+    my ($resurl,$forcereg,$group,$bread_crumbs,$advtools,$docscrumbs,$hostname) = @_;
     unless ($env{'request.registered'}) {
         undef(@inlineremote);
     }
@@ -1077,8 +1209,9 @@ sub prepare_functions {
     }
 
     my $editbutton = '';
+    my $viewsrcbutton = '';
 #
-# Determine whether or not to display 'Edit' icon/button
+# Determine whether or not to display 'Edit' or 'View Source' icon/button
 #
     if ($resurl =~ m{^/?adm/($match_domain)/($match_username)/aboutme$}) {
         my $file=&Apache::lonnet::declutter($env{'request.filename'});
@@ -1109,17 +1242,31 @@ sub prepare_functions {
 # This applies in course context
 #
         if (($perms{'mdc'}) &&
-            (($resurl eq "/public/$cdom/$cnum/syllabus") ||
-            ($resurl =~ m{^/uploaded/$cdom/$cnum/portfolio/syllabus/}))) {
-            $cfile = $resurl;
+            (($resurl =~ m{^/?public/$cdom/$cnum/syllabus}) ||
+             ($resurl =~ m{^/?uploaded/$cdom/$cnum/portfolio/syllabus/}) ||
+             (($resurl =~ m{^/?uploaded/$cdom/$cnum/default_\d+\.sequence$}) && ($env{'form.navmap'})))) {
+            if ($resurl =~ m{^/}) {
+                $cfile = $resurl;
+            } else {
+                $cfile = "/$resurl";
+            }
             $home = &Apache::lonnet::homeserver($cnum,$cdom);
             if ($env{'form.forceedit'}) {
                 $forceview = 1;
             } else {
                 $forceedit = 1;
             }
-            $editbutton = &get_editbutton($cfile,$home,$switchserver,
-                                          $forceedit,$forceview,$forcereg);
+            if ($cfile =~ m{^/uploaded/$cdom/$cnum/default_\d+\.sequence$}) {
+                my $text = 'Edit Folder';
+                &switch('','',7,4,'docs-22x22.png','Edit Folder','parms[_2]',
+                        "gocmd('/adm/coursedocs','direct')",
+                        'Folder/Page Content');
+                $editbutton = 1;
+            } else {
+                $editbutton = &get_editbutton($cfile,$home,$switchserver,
+                                              $forceedit,$forceview,$forcereg,
+                                              $hostname);
+            }
         } elsif (($resurl eq '/adm/extresedit') &&
                  (($env{'form.symb'}) || ($env{'form.folderpath'}))) {
             ($cfile,$home,$switchserver,$forceedit,$forceview) =
@@ -1127,8 +1274,7 @@ sub prepare_functions {
                                                $env{'form.symb'});
             if ($cfile ne '') {
                 $editbutton = &get_editbutton($cfile,$home,$switchserver,
-                                              $forceedit,$forceview,$forcereg,
-                                              $env{'form.title'},$env{'form.suppurl'});
+                                              $forceedit,$forceview,$forcereg);
             }
         } elsif (($resurl =~ m{^/?adm/viewclasslist$}) &&
                  (&Apache::lonnet::allowed('opa',$env{'request.course.id'}))) {
@@ -1146,7 +1292,31 @@ sub prepare_functions {
                         &Apache::lonnet::clutter($resurl),$env{'request.symb'},$group);
                 if ($cfile ne '') {
                     $editbutton = &get_editbutton($cfile,$home,$switchserver,
-                                                  $forceedit,$forceview,$forcereg);
+                                                  $forceedit,$forceview,$forcereg,
+                                                  $hostname);
+                }
+                if ((($cfile eq '') || (!$editbutton)) &&
+                    ($resurl =~ /$LONCAPA::assess_re/)) {
+                    my $showurl = &Apache::lonnet::clutter($resurl);
+                    if ((&Apache::lonnet::allowed('cre','/')) &&
+                        (&Apache::lonnet::metadata($resurl,'sourceavail') eq 'open')) {
+                        $viewsrcbutton = 1;
+                    } elsif (&Apache::lonnet::allowed('vxc',$env{'request.course.id'})) {
+                        if ($showurl =~ m{^\Q/res/$cdom/\E($match_username)/}) {
+                            my $auname = $1;
+                            if (($env{'request.course.adhocsrcaccess'} ne '') &&
+                                (grep(/^\Q$auname\E$/,split(/,/,$env{'request.course.adhocsrcaccess'})))) {
+                                $viewsrcbutton = 1;
+                            } elsif ((&Apache::lonnet::metadata($resurl,'sourceavail') eq 'open') &&
+                                     (&Apache::lonnet::allowed('bre','/'))) {
+                                $viewsrcbutton = 1;
+                            }
+                        }
+                    }
+                    if ($viewsrcbutton) {
+                        &switch('','',6,1,'pcstr.png','View Source','resource[_2]','open_source()',
+                                'View source code');
+                    }
                 }
             }
         }
@@ -1196,17 +1366,25 @@ sub prepare_functions {
         if (($env{'form.folderpath'} =~ /^supplemental/) &&
             (&Apache::lonnet::allowed('mdc',$env{'request.course.id'})) &&
             (($resurl =~ m{^/adm/wrapper/ext/}) ||
-             ($resurl =~ m{^/adm/$cdom/$cnum/\d+/exttools?$}) ||
+             ($resurl =~ m{^/adm/$cdom/$cnum/\d+/ext\.tool$}) ||
              ($resurl =~ m{^/uploaded/$cdom/$cnum/supplemental/}) ||
              ($resurl eq '/adm/supplemental') ||
              ($resurl =~ m{^/public/$cdom/$cnum/syllabus$}) ||
              ($resurl =~ m{^/adm/$match_domain/$match_username/aboutme$}))) {
             my @folders=split('&',$env{'form.folderpath'});
             if ((@folders > 2) || ($resurl ne '/adm/supplemental')) {
+                my $suppanchor;
+                if ($resurl =~ m{^/adm/wrapper/ext/}) {
+                    $suppanchor = $env{'form.anchor'};
+                }
                 my $esc_path=&escape(&HTML::Entities::encode(&escape($env{'form.folderpath'}),'<>&"'));
+                my $link = '/adm/coursedocs?command=direct&amp;forcesupplement=1&amp;supppath='.
+                           "$esc_path&amp;anchor=$suppanchor";
+                if ($env{'request.use_absolute'} ne '') {
+                    $link = $env{'request.use_absolute'}.$link;
+                }
                 &switch('','',7,4,'docs-22x22.png','Edit Folder','parms[_2]',
-                        "location.href='/adm/coursedocs?command=direct&amp;forcesupplement=1&amp;supppath=$esc_path'",
-                        'Folder/Page Content');
+                        "location.href='$link'",'Folder/Page Content');
             }
         }
     }
@@ -1217,7 +1395,7 @@ sub prepare_functions {
         &advtools_crumbs(@inlineremote);
         return $editbutton;
     } elsif ($env{'request.registered'}) {
-        return $editbutton;
+        return $editbutton || $viewsrcbutton;
     } else {
         if (ref($bread_crumbs) eq 'ARRAY') {
             if (@inlineremote > 0) {
@@ -2024,6 +2202,12 @@ function open_StoredLinks_Import(rat) {
    newWin.focus();
 }
 
+function open_source() {
+   var url = escape(window.location.pathname);
+   sourcewin=window.open('/adm/source?inhibitmenu=yes&viewonly=1&filename='+url,'LONsource',
+                         'height=500,width=600,resizable=yes,location=no,menubar=no,toolbar=no,scrollbars=yes');
+}
+
 (function (\$) {
   \$(document).ready(function () {
     \$.single=function(a){return function(b){a[0]=b;return a}}(\$([1]));
@@ -2044,8 +2228,12 @@ ENDUTILITY
 }
 
 sub serverform {
+    my $target;
+    unless (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
+        $target = ' target="_top"';
+    }
     return(<<ENDSERVERFORM);
-<form name="server" action="/adm/logout" method="post" target="_top">
+<form name="server" action="/adm/logout" method="post"$target>
 <input type="hidden" name="postdata" value="none" />
 <input type="hidden" name="command" value="none" />
 <input type="hidden" name="url" value="none" />
@@ -2083,10 +2271,10 @@ sub hidden_button_check {
 }
 
 sub roles_selector {
-    my ($cdom,$cnum,$httphost) = @_;
+    my ($cdom,$cnum,$httphost,$ltitarget) = @_;
     my $crstype = &Apache::loncommon::course_type();
     my $now = time;
-    my (%courseroles,%seccount,%courseprivs);
+    my (%courseroles,%seccount,%courseprivs,%roledesc);
     my $is_cc;
     my ($js,$form,$switcher);
     my $ccrole;
@@ -2095,13 +2283,13 @@ sub roles_selector {
     } else {
         $ccrole = 'cc';
     }
-    my ($priv,$gotsymb,$destsymb);
+    my ($privref,$gotsymb,$destsymb);
     my $destinationurl = $ENV{'REQUEST_URI'};
-    if ($destinationurl =~ /\?symb=/) {
+    if ($destinationurl =~ /(\?|\&)symb=/) {
         $gotsymb = 1;
     } elsif ($destinationurl =~ m{^/enc/}) {
         my $plainurl = &Apache::lonenc::unencrypted($destinationurl);
-        if ($plainurl =~ /\?symb=/) {
+        if ($plainurl =~ /(\?|\&)symb=/) {
             $gotsymb = 1;
         }
     }
@@ -2116,12 +2304,15 @@ sub roles_selector {
         my $destination = $destinationurl;
         $destination =~ s/(\?.*)$//;
         if (exists($reqprivs->{$destination})) {
-            $priv = $reqprivs->{$destination};
+            if ($reqprivs->{$destination} =~ /,/) {
+                @{$privref} = split(/,/,$reqprivs->{$destination});
+            } else { 
+                $privref = [$reqprivs->{$destination}];
+            }
         }
     }
     if ($env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum}) {
         my ($start,$end) = split(/\./,$env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum});
-        
         if ((($start) && ($start<0)) || 
             (($end) && ($end<$now))  ||
             (($start) && ($now<$start))) {
@@ -2131,7 +2322,9 @@ sub roles_selector {
         }
     }
     if ($is_cc) {
-        &get_all_courseroles($cdom,$cnum,\%courseroles,\%seccount,\%courseprivs,$priv);
+        &get_all_courseroles($cdom,$cnum,\%courseroles,\%seccount,\%courseprivs);
+    } elsif ($env{'request.role'} =~ m{^\Qcr/$cdom/$cdom-domainconfig/\E(\w+)\.\Q/$cdom/$cnum\E}) {
+        &get_customadhoc_roles($cdom,$cnum,\%courseroles,\%seccount,\%courseprivs,\%roledesc,$privref);
     } else {
         my %gotnosection;
         foreach my $item (keys(%env)) {
@@ -2147,7 +2340,7 @@ sub roles_selector {
                         $gotnosection{$role} = 1;
                     }
                 }
-                if ($priv ne '') {
+                if ((ref($privref) eq 'ARRAY') && (@{$privref} > 0)) {
                     my $cnumsec = $cnum;
                     if ($sec ne '') {
                         $cnumsec .= "/$sec";
@@ -2185,7 +2378,7 @@ sub roles_selector {
     }
     if ((keys(%seccount) > 1) || ($numdiffsec > 1)) {
         my @submenu;
-        $js = &jump_to_role($cdom,$cnum,\%seccount,\%courseroles,\%courseprivs,$priv);
+        $js = &jump_to_role($cdom,$cnum,\%seccount,\%courseroles,\%courseprivs,\%roledesc,$privref);
         $form = 
             '<form name="rolechooser" method="post" action="'.$httphost.'/adm/roles">'."\n".
             '  <input type="hidden" name="destinationurl" value="'.
@@ -2225,13 +2418,22 @@ sub roles_selector {
                     $include = 1; 
                 }
                 if ($include) {
+                    my $rolename;
+                    if ($role =~ m{^cr/$cdom/$cdom\-domainconfig/(\w+)(?:/\w+|$)}) {
+                        $rolename = $roledesc{$role};
+                        if ($rolename eq '') {
+                            $rolename = &mt('Helpdesk [_1]',$1);
+                        }
+                    } else {
+                        $rolename = &Apache::lonnet::plaintext($role);
+                    }
                     push(@submenu,['javascript:adhocRole('."'$role'".')',
-                                   &Apache::lonnet::plaintext($role)]);
+                                   $rolename]);
                 }
             }
         }
         if (@submenu > 0) {
-            $switcher = &create_submenu('','',&mt('Switch role'),\@submenu);
+            $switcher = &create_submenu('','',&mt('Switch role'),\@submenu,'','',$ltitarget);
         }
     }
     return ($js,$form,$switcher);
@@ -2307,8 +2509,101 @@ sub get_all_courseroles {
     return;
 }
 
+sub get_customadhoc_roles {
+    my ($cdom,$cnum,$courseroles,$seccount,$courseprivs,$roledesc,$privref) = @_;
+    unless ((ref($courseroles) eq 'HASH') && (ref($seccount) eq 'HASH') &&
+            (ref($courseprivs) eq 'HASH') && (ref($roledesc) eq 'HASH')) {
+        return;
+    }
+    my $is_helpdesk = 0;
+    my $now = time;
+    foreach my $role ('dh','da') {
+        if ($env{"user.role.$role./$cdom/"}) {
+            my ($start,$end)=split(/\./,$env{"user.role.$role./$cdom/"});
+            if (!($start && ($now<$start)) && !($end && ($now>$end))) {
+                $is_helpdesk = 1;
+                last;
+            }
+        }
+    }
+    if ($is_helpdesk) {
+        my ($possroles,$description) = &Apache::lonnet::get_my_adhocroles($cdom.'_'.$cnum);
+        my %available;
+        if (ref($possroles) eq 'ARRAY') {
+            map { $available{$_} = 1; } @{$possroles};
+        }
+        my %domdefaults = &Apache::lonnet::get_domain_defaults($cdom);
+        if (ref($domdefaults{'adhocroles'}) eq 'HASH') {
+            if (keys(%{$domdefaults{'adhocroles'}})) {
+                my $numsec = 1;
+                my @sections;
+                my ($allseclist,$cached) =
+                    &Apache::lonnet::is_cached_new('courseseclist',$cdom.'_'.$cnum);
+                if (defined($cached)) {
+                    if ($allseclist ne '') {
+                        @sections = split(/,/,$allseclist);
+                        $numsec += scalar(@sections);
+                    }
+                } else {
+                    my %sections_count = &Apache::loncommon::get_sections($cdom,$cnum);
+                    @sections = sort(keys(%sections_count));
+                    $numsec += scalar(@sections);
+                    $allseclist = join(',',@sections);
+                    &Apache::lonnet::do_cache_new('courseseclist',$cdom.'_'.$cnum,$allseclist);
+                }
+                my (%adhoc,$gotprivs);
+                my $prefix = "cr/$cdom/$cdom".'-domainconfig';
+                foreach my $role (keys(%{$domdefaults{'adhocroles'}})) {
+                    next if (($role eq '') || ($role =~ /\W/));
+                    $seccount->{"$prefix/$role"} = $numsec;
+                    $roledesc->{"$prefix/$role"} = $description->{$role};
+                    if ((ref($privref) eq 'ARRAY') && (@{$privref} > 0)) {
+                        if (exists($env{"user.priv.$prefix/$role./$cdom/$cnum./"})) {
+                            $courseprivs->{"$prefix/$role./$cdom/$cnum./"} =
+                                $env{"user.priv.$prefix/$role./$cdom/$cnum./"};
+                            $courseprivs->{"$prefix/$role./$cdom/$cnum./$cdom/"} =
+                                $env{"user.priv.$prefix/$role./$cdom/$cnum./$cdom/"};
+                            $courseprivs->{"$prefix/$role./$cdom/$cnum./$cdom/$cnum"} =
+                                $env{"user.priv.$prefix/$role./$cdom/$cnum./$cdom/$cnum"};
+                        } else {
+                            unless ($gotprivs) {
+                                my ($adhocroles,$privscached) =
+                                    &Apache::lonnet::is_cached_new('adhocroles',$cdom);
+                                if ((defined($privscached)) && (ref($adhocroles) eq 'HASH')) {
+                                    %adhoc = %{$adhocroles};
+                                } else {
+                                    my $confname = &Apache::lonnet::get_domainconfiguser($cdom);
+                                    my %roledefs = &Apache::lonnet::dump('roles',$cdom,$confname,'rolesdef_');
+                                    foreach my $key (keys(%roledefs)) {
+                                        (undef,my $rolename) = split(/_/,$key);
+                                        if ($rolename ne '') {
+                                            my ($systempriv,$domainpriv,$coursepriv) = split(/\_/,$roledefs{$key});
+                                            $coursepriv = &Apache::lonnet::course_adhocrole_privs($rolename,$cdom,$cnum,$coursepriv);
+                                            $adhoc{$rolename} = join('_',($systempriv,$domainpriv,$coursepriv));
+                                        }
+                                    }
+                                    &Apache::lonnet::do_cache_new('adhocroles',$cdom,\%adhoc);
+                                }
+                                $gotprivs = 1;
+                            }
+                            ($courseprivs->{"$prefix/$role./$cdom/$cnum./"},
+                             $courseprivs->{"$prefix/$role./$cdom/$cnum./$cdom/"},
+                             $courseprivs->{"$prefix/$role./$cdom/$cnum./$cdom/$cnum"}) =
+                                 split(/\_/,$adhoc{$role});
+                        }
+                    }
+                    if ($available{$role}) {
+                        $courseroles->{"$prefix/$role"} = \@sections;
+                    }
+                }
+            }
+        }
+    }
+    return;
+}
+
 sub jump_to_role {
-    my ($cdom,$cnum,$seccount,$courseroles,$courseprivs,$priv) = @_;
+    my ($cdom,$cnum,$seccount,$courseroles,$courseprivs,$roledesc,$privref) = @_;
     my %lt = &Apache::lonlocal::texthash(
                 this => 'This role has section(s) associated with it.',
                 ente => 'Enter a specific section.',
@@ -2341,29 +2636,26 @@ sub jump_to_role {
         }
     }
     my $checkroles = 0;
-    if ($priv && ref($courseprivs) eq 'HASH') {
-        my (%disallowed,%allowed,@disallow);
+    if ((ref($privref) eq 'ARRAY') && (@{$privref} > 0) && (ref($courseprivs) eq 'HASH')) {
+        my %disallowed;
         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;
+                $disallowed{$trole} = 1;
+                foreach my $priv (@{$privref}) { 
+                    if ($courseprivs->{$role} =~ /\Q:$priv\E($|:|\&\w+)/) {
+                        delete($disallowed{$trole});
+                        last;
+                    }
                 }
             }
         }
-        foreach my $trole (keys(%disallowed)) {
-            unless ($allowed{$trole}) {
-                push(@disallow,$trole);
-            }
-        }
-        if (@disallow > 0) {
+        if (keys(%disallowed) > 0) {
             $checkroles = 1;
-            $js .= "    var disallow = new Array('".join("','",@disallow)."');\n".
+            $js .= "    var disallow = new Array('".join("','",keys(%disallowed))."');\n".
                    "    var rolecheck = 1;\n";
         }
     }
@@ -2460,13 +2752,13 @@ END
 
 sub required_privs {
     my $privs =  {
-             '/adm/parmset'      => 'opa',
-             '/adm/courseprefs'  => 'opa',
+             '/adm/parmset'      => 'opa,vpa',
+             '/adm/courseprefs'  => 'opa,vpa',
              '/adm/whatsnew'     => 'whn',
-             '/adm/populate'     => 'cst',
+             '/adm/populate'     => 'cst,vpa,vcl',
              '/adm/trackstudent' => 'vsa',
-             '/adm/statistics'   => 'vgr',
-             '/adm/setblock'     => 'dcm',
+             '/adm/statistics'   => 'mgr,vgr',
+             '/adm/setblock'     => 'dcm,vcb',
              '/adm/coursedocs'   => 'mdc',
            };
     unless ($env{'course.'.$env{'request.course.id'}.'.grading'} eq 'spreadsheet') {
@@ -2479,16 +2771,26 @@ sub required_privs {
 
 sub countdown_timer {
     if (($env{'request.course.id'}) && ($env{'request.symb'} ne '') &&
-        ($env{'request.filename'}=~/$LONCAPA::assess_re/)) {
+        (($env{'request.filename'}=~/$LONCAPA::assess_re/) ||
+         (($env{'request.symb'} =~ /ext\.tool$/) &&
+         (&Apache::lonnet::EXT('resource.0.gradable',$env{'request.symb'}) =~ /^yes$/i)))) {
         my ($type,$hastimeleft,$slothastime);
         my $now = time;
         if ($env{'request.filename'} =~ /\.task$/) {
             $type = 'Task';
+        } elsif ($env{'request.symb'} =~ /ext\.tool$/) {
+            $type = 'tool';
         } else {
             $type = 'problem';
         }
-        my ($status,$accessmsg,$slot_name,$slot) =
-            &Apache::lonhomework::check_slot_access('0',$type);
+        my ($status,$accessmsg,$slot_name,$slot);
+        if ($type eq 'tool') {
+            ($status,$accessmsg,$slot_name,$slot) =
+                &Apache::lonhomework::check_slot_access('0',$type,$env{'request.symb'},['0']);
+        } else {
+            ($status,$accessmsg,$slot_name,$slot) =
+                &Apache::lonhomework::check_slot_access('0',$type);
+        }
         if ($slot_name ne '') {
             if (ref($slot) eq 'HASH') {
                 if (($slot->{'starttime'} < $now) &&
@@ -2590,6 +2892,9 @@ BEGIN {
                     } elsif ($configline=~/^scnd\:/) {
                         my @entries = (split(/\:/, $configline))[1..5];
                         push(@secondary_menu,\@entries);
+                    } elsif ($configline=~/^scndsub\:/) {
+                        my ($parent,@entries) = (split(/\:/, $configline))[1..4];
+                        push(@{$secondary_submenu{$parent}},\@entries);
                     } elsif ($configline) {
                         push(@desklines,$configline);
                     }