--- loncom/interface/lonmenu.pm 2017/02/18 23:39:16 1.467 +++ loncom/interface/lonmenu.pm 2018/04/15 00:28:07 1.487 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Routines to control the menu # -# $Id: lonmenu.pm,v 1.467 2017/02/18 23:39:16 raeburn Exp $ +# $Id: lonmenu.pm,v 1.487 2018/04/15 00:28:07 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 '
  • $link
  • |; + . qq| href="$$menuitem[0]"$target>$link|; } # 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} .= '
  • '.&Apache::loncommon::top_nav_help('Help').'
  • '; } } else { - $menu{$position} .= prep_menuitem($menuitem); + $menu{$position} .= prep_menuitem($menuitem,$ltitarget); } } my @output = ('',''); @@ -401,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 || $canvieweditor) { - $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'}) || @@ -444,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 @@ -471,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' @@ -483,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 .= '
  • '.&mt($title).'
  • '; + } + } + } 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 { @@ -497,18 +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{'SERVER_PORT'} == 443) && - ($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'} =~ m{^http://})) { - $$menuitem[0] .= ($$menuitem[0]=~/\?/) ? '&' : '?' . 'usehttp=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\]/) { @@ -525,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; } @@ -616,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 .= '
  • '; @@ -629,7 +682,7 @@ sub build_submenu { } sub innerregister { - my ($forcereg,$bread_crumbs,$group,$pagebuttonshide) = @_; + my ($forcereg,$bread_crumbs,$group,$pagebuttonshide,$hostname) = @_; my $const_space = ($env{'request.state'} eq 'construct'); my $is_const_dir = 0; @@ -639,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(); @@ -696,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 { @@ -711,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') { @@ -738,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); @@ -753,8 +837,14 @@ sub innerregister { $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')); @@ -769,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')", @@ -854,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"; @@ -912,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.=(<&"')); + my $link = '/adm/coursedocs?command=direct&forcesupplement=1&supppath='. + "$esc_path&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&forcesupplement=1&supppath=$esc_path&anchor=$suppanchor'", - 'Folder/Page Content'); + "location.href='$link'",'Folder/Page Content'); } } } @@ -1234,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) { @@ -1892,6 +2053,11 @@ sub utilityfunctions { my $countdown = &countdown_toggle_js(); + my $ltitarget; + if ($env{'request.lti.login'}) { + $ltitarget = $env{'request.lti.target'}; + } + my $hostvar = ' function setLCHost() { var lcHostname=""; @@ -1988,7 +2154,12 @@ function golist(url) { currentURL = null; currentSymb= null; var lcHostname = setLCHost(); - top.location.href=lcHostname+url; + var ltitarget = '$ltitarget'; + if (ltitarget == 'iframe') { + document.location.href=lcHostname+url; + } else { + top.location.href=lcHostname+url; + } } } @@ -2041,6 +2212,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])); @@ -2061,8 +2238,12 @@ ENDUTILITY } sub serverform { + my $target; + unless (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) { + $target = ' target="_top"'; + } return(< +
    @@ -2072,15 +2253,20 @@ ENDSERVERFORM } sub constspaceform { + my ($target,$printtarget); + unless (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) { + $target = ' target="_top"'; + $printtarget = ' target="_parent"'; + } return(< + -
    +
    -
    + @@ -2100,7 +2286,7 @@ 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,%roledesc); @@ -2114,11 +2300,11 @@ sub roles_selector { } 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; } } @@ -2262,7 +2448,7 @@ sub roles_selector { } } if (@submenu > 0) { - $switcher = &create_submenu('','',&mt('Switch role'),\@submenu); + $switcher = &create_submenu('','',&mt('Switch role'),\@submenu,'','',$ltitarget); } } return ($js,$form,$switcher); @@ -2600,16 +2786,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) && @@ -2711,6 +2907,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); }