--- loncom/interface/lonmenu.pm 2017/09/04 19:17:02 1.476 +++ loncom/interface/lonmenu.pm 2022/06/30 21:04:14 1.524 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Routines to control the menu # -# $Id: lonmenu.pm,v 1.476 2017/09/04 19:17:02 raeburn Exp $ +# $Id: lonmenu.pm,v 1.524 2022/06/30 21:04:14 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,$target,$listclass,$linkattr) This routine wraps a menuitem in proper HTML. It is used by primary_menu() and secondary_menu(). @@ -210,18 +210,19 @@ 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,$target,$listclass,$linkattr) = @_; return '' unless(ref($menuitem) eq 'ARRAY'); - my $link; + my ($link,$targetattr); if ($$menuitem[1]) { # graphical Link $link = "':'
  • ').'$link
  • |; + . qq| href="$$menuitem[0]"$targetattr $linkattr>$link|; } # primary_menu() evaluates @primary_menu and returns a two item array, @@ -242,8 +246,8 @@ sub prep_menuitem { # @primary_menu is filled within the BEGIN block of this module with # entries from mydesk.tab sub primary_menu { - my ($crstype) = @_; - my (%menu); + my ($crstype,$ltimenu,$menucoll,$menuref,$links_disabled,$links_target) = @_; + my (%menu,%ltiexc,%menuopts); # each element of @primary contains following array: # (link url, icon path, alt text, link text, condition, position) my $public; @@ -260,6 +264,41 @@ sub primary_menu { my %roles_in_env; $rolecount = &Apache::lonroles::roles_from_env(\%roles_in_env,$update); } + my $lti; + if ($env{'request.lti.login'}) { + $lti = 1; + if (ref($ltimenu) eq 'HASH') { + foreach my $item ('fullname','logout') { + unless ($ltimenu->{$item}) { + $ltiexc{$item} = 1; + } + } + } + } + my ($listclass,$linkattr,$target); + if ($links_disabled) { + $listclass = 'LCisDisabled'; + $linkattr = 'aria-disabled="true"'; + } + if ($links_target ne '') { + $target = $links_target; + } else { + my ($ltitarget,$deeplinktarget); + if ($env{'request.lti.login'}) { + $ltitarget = $env{'request.lti.target'}; + } + if ($env{'request.deeplink.login'}) { + $deeplinktarget = $env{'request.deeplink.target'}; + } + if (($ltitarget eq 'iframe') || ($deeplinktarget eq '_self')) { + $target = '_self'; + } else { + $target = '_top'; + } + } + if (($menucoll) && (ref($menuref) eq 'HASH')) { + %menuopts = %{$menuref}; + } foreach my $menuitem (@primary_menu) { # evaluate conditions next if ref($menuitem) ne 'ARRAY'; # @@ -274,9 +313,15 @@ sub primary_menu { && !$public; # only visible to public # users next if $$menuitem[4] eq 'roles' ##show links depending on - && &Apache::loncommon::show_course(); ##term 'Courses' or - next if $$menuitem[4] eq 'courses' ##'Roles' wanted - && !&Apache::loncommon::show_course(); ## + && (&Apache::loncommon::show_course() ##term 'Courses' or + || $lti); ##'Roles' wanted + next if $$menuitem[4] eq 'courses' ##and not LTI access + && (!&Apache::loncommon::show_course() + || $lti); + next if $$menuitem[4] eq 'notlti' + && $lti; + next if $$menuitem[4] eq 'ltiexc' + && exists($ltiexc{lc($menuitem->[3])}); my $title = $menuitem->[3]; if (($crstype eq 'Placement') && (!$env{'request.role.adv'})) { if ($menuitem->[4] eq 'courses') { @@ -289,11 +334,27 @@ sub primary_menu { if ($position eq '') { $position = 'right'; } + if ($env{'request.course.id'} && $menucoll) { + if (($menuitem->[6]) && (!$menuopts{$menuitem->[6]})) { + if ($menuitem->[6] eq 'pers') { + if ($menuopts{'name'} && !$ltiexc{'fullname'} && + $env{'user.name'} && $env{'user.domain'}) { + $menu{$position} .= '
  • '. + &Apache::loncommon::plainname($env{'user.name'}, + $env{'user.domain'}).'
  • '; + next; + } else { + next; + } + } else { + next; + } + } + } if (defined($primary_submenu{$title})) { - my ($link,$target); + my $link; if ($menuitem->[0] ne '') { $link = $menuitem->[0]; - $target = '_top'; } else { $link = '#'; } @@ -306,17 +367,30 @@ sub primary_menu { ($item->[2] eq 'blog')) && (!&Apache::lonnet::usertools_access('','',$item->[2], undef,'tools'))); + if ($env{'request.course.id'} && $menucoll) { + next if ($item->[3]) && (!$menuopts{$item->[3]}); + } push(@primsub,$item); } - if ($title eq 'Personal' && $env{'user.name'} && $env{'user.domain'} ) { - $title = &Apache::loncommon::plainname($env{'user.name'},$env{'user.domain'}); + if ($title eq 'Personal') { + if ($env{'user.name'} && $env{'user.domain'} && !$ltiexc{'fullname'}) { + unless (($env{'request.course.id'}) && ($menucoll) && (!$menuopts{'name'})) { + $title = &Apache::loncommon::plainname($env{'user.name'},$env{'user.domain'}); + } + } + next if (($env{'request.course.id'}) && ($menucoll) && ($title eq 'Personal') && + (!@primsub)); + if ($title eq 'Personal') { + $title = &mt($title); + } } else { $title = &mt($title); } if (@primsub > 0) { - $menu{$position} .= &create_submenu($link,$target,$title,\@primsub,1); + $menu{$position} .= &create_submenu($link,$target,$title,\@primsub,1,undef,$listclass,$linkattr); } elsif ($link) { - $menu{$position} .= '
  • '.$title.'
  • '; + $menu{$position} .= ($listclass?'
  • ':'
  • '). + ''.$title.'
  • '; } } } elsif ($$menuitem[3] eq 'Help') { # special treatment for helplink @@ -328,13 +402,22 @@ sub primary_menu { 'helpdeskmail', $defdom,$origmail); if ($to ne '') { - $menu{$position} .= &prep_menuitem($menuitem); + $menu{$position} .= &prep_menuitem($menuitem,$target,$listclass,$linkattr); } } else { - $menu{$position} .= '
  • '.&Apache::loncommon::top_nav_help('Help').'
  • '; + $menu{$position} .= ($listclass?'
  • ':'
  • '). + &Apache::loncommon::top_nav_help('Help',$linkattr). + '
  • '; + } + } elsif ($$menuitem[3] eq 'Log In') { + if ($public) { + if (&Apache::lonnet::get_saml_landing()) { + $$menuitem[0] = '/adm/login'; + } } + $menu{$position} .= prep_menuitem($menuitem,$target,$listclass,$linkattr); } else { - $menu{$position} .= prep_menuitem($menuitem); + $menu{$position} .= prep_menuitem($menuitem,$target,$listclass,$linkattr); } } my @output = ('',''); @@ -373,7 +456,8 @@ sub getauthor{ } sub secondary_menu { - my ($httphost) = @_; + my ($httphost,$ltiscope,$ltimenu,$noprimary,$menucoll,$menuref, + $links_disabled,$links_target) = @_; my $menu; my $crstype = &Apache::loncommon::course_type(); @@ -394,17 +478,16 @@ sub secondary_menu { my $canmodpara = &Apache::lonnet::allowed('opa', $crs_sec); my $canvgr = &Apache::lonnet::allowed('vgr', $crs_sec); my $canmgr = &Apache::lonnet::allowed('mgr', $crs_sec); + my $canplc = &Apache::lonnet::allowed('plc', $crs_sec); my $author = &getauthor(); - my ($cdom,$cnum,$showsyllabus,$showfeeds,$showresv,$grouptools); + my ($cdom,$cnum,$showsyllabus,$showfeeds,$showresv,$grouptools, + $lti,$ltimapres,%ltiexc,%menuopts); $grouptools = 0; 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'}) || @@ -423,10 +506,9 @@ sub secondary_menu { $showresv = 1; } } - my %groups = &Apache::lonnet::get_active_groups( - $env{'user.domain'}, $env{'user.name'},$cdom,$cnum); - if (%groups) { - foreach my $group (keys(%groups)) { + if ($env{'request.course.groups'} ne '') { + foreach my $group (split(/:/,$env{'request.course.groups'})) { + next unless ($group =~ /^\w+$/); my @privs = split(/:/,$env{"user.priv.$env{'request.role'}./$cdom/$cnum/$group"}); shift(@privs); if (@privs) { @@ -434,6 +516,28 @@ sub secondary_menu { } } } + if ($env{'request.lti.login'}) { + $lti = 1; + if (ref($ltimenu) eq 'HASH') { + foreach my $item ('fullname','coursetitle','role','logout','grades') { + unless ($ltimenu->{$item}) { + $ltiexc{$item} = 1; + } + } + } + if (($ltiscope eq 'map') || ($ltiscope eq 'resource')) { + $ltimapres = 1; + } + } + } + if (($menucoll) && (ref($menuref) eq 'HASH')) { + %menuopts = %{$menuref}; + } + + my ($listclass,$linkattr,$target); + if ($links_disabled) { + $listclass = 'LCisDisabled'; + $linkattr = 'aria-disabled="true"'; } my ($canmodifycoauthor); @@ -444,7 +548,24 @@ sub secondary_menu { $canmodifycoauthor = 1; } } + my ($roleswitcher_js,$roleswitcher_form); + if ($links_target ne '') { + $target = $links_target; + } else { + my ($ltitarget,$deeplinktarget); + if ($env{'request.lti.login'}) { + $ltitarget = $env{'request.lti.target'}; + } + if ($env{'request.deeplink.login'}) { + $deeplinktarget = $env{'request.deeplink.target'}; + } + if (($ltitarget eq 'iframe') || ($deeplinktarget eq '_self')) { + $target = '_self'; + } else { + $target = '_top'; + } + } foreach my $menuitem (@secondary_menu) { # evaluate conditions @@ -456,7 +577,7 @@ sub secondary_menu { next if $$menuitem[4] =~ /^crsedit/ && (!$canedit && !$canvieweditor); next if $$menuitem[4] eq 'nvgr' - && $canvgr; + && ($canvgr || $ltiexc{'grades'}); next if $$menuitem[4] eq 'vgr' && !$canvgr; next if $$menuitem[4] eq 'viewusers' @@ -471,27 +592,94 @@ 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' && !$showfeeds; + next if $$menuitem[4] eq 'plc' + && !$canplc; next if $$menuitem[4] eq 'author' && !$author; next if $$menuitem[4] eq 'cca' && !$canmodifycoauthor; + next if $$menuitem[4] eq 'notltimapres' + && $ltimapres; + next if $$menuitem[4] eq 'notlti' + && $lti; + next if $$menuitem[4] eq 'lti' + && (!$lti || !$noprimary); + next if $$menuitem[3] eq 'Logout' + && $ltiexc{'logout'}; - if ($$menuitem[3] eq 'Roles' && $env{'request.course.id'}) { + my $title = $menuitem->[3]; + if ($env{'request.course.id'} && $menucoll) { + unless ($$menuitem[5] eq 'roles') { + next if (($$menuitem[5]) && (!$menuopts{$$menuitem[5]})); + } + } + if (defined($secondary_submenu{$title})) { + my $link; + if ($menuitem->[0] ne '') { + $link = $menuitem->[0]; + } 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); + next if ($item->[2] eq 'lti' && !$lti); + if ($item->[2] =~ /^lti(portfolio|wishlist|blog)$/) { + my $tool = $1; + next if !$lti; + next if (!&Apache::lonnet::usertools_access('','',$tool, + undef,'tools')); + } + push(@scndsub,$item); + } + } + if ($title eq 'Personal' && $env{'user.name'} && $env{'user.domain'}) { + unless ($ltiexc{'fullname'}) { + $title = &Apache::loncommon::plainname($env{'user.name'},$env{'user.domain'}); + } + } + if (@scndsub > 0) { + $menu .= &create_submenu($link,$target,&mt($title),\@scndsub,1,undef, + $listclass,$linkattr); + } elsif ($link ne '#') { + $menu .= ($listclass?'
  • ':'
  • '). + ''. + &mt($title).'
  • '; + } + } + } elsif ($$menuitem[3] eq 'Roles' && $env{'request.course.id'}) { # special treatment for role selector - ($roleswitcher_js,$roleswitcher_form,my $switcher) = + my ($switcher,$has_opa_priv); + ($roleswitcher_js,$roleswitcher_form,$switcher,$has_opa_priv) = &roles_selector( - $env{'course.' . $env{'request.course.id'} . '.domain'}, - $env{'course.' . $env{'request.course.id'} . '.num'}, - $httphost + $env{'course.' . $env{'request.course.id'} . '.domain'}, + $env{'course.' . $env{'request.course.id'} . '.num'}, + $httphost,$target,$menucoll,$menuref ); + if (($$menuitem[5]) && (!$menuopts{$$menuitem[5]})) { + next unless ($has_opa_priv); + } $menu .= $switcher; + } elsif ($$menuitem[3] eq 'Help') { # special treatment for helplink + next if ($crstype eq 'Placement'); + $menu .= '
  • '.&Apache::loncommon::top_nav_help('Help').'
  • '; } else { if ($$menuitem[3] eq 'Syllabus' && $env{'request.course.id'}) { my $url = $$menuitem[0]; @@ -505,17 +693,19 @@ sub secondary_menu { } 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'; + unless ((&Apache::lonnet::uses_sts()) || (&Apache::lonnet::waf_allssl())) { + 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,$target,$listclass,$linkattr); } } if ($menu =~ /\[url\].*\[symb\]/) { @@ -538,7 +728,8 @@ sub secondary_menu { } $menu =~ s/\[uname\]/$$author{user}/g; $menu =~ s/\[udom\]/$$author{dom}/g; - if ($showsyllabus || $showfeeds) { + $menu =~ s/\[javascript\]/javascript:/g; + if ($env{'request.course.id'}) { $menu =~ s/\[cnum\]/$cnum/g; $menu =~ s/\[cdom\]/$cdom/g; } @@ -552,14 +743,14 @@ sub secondary_menu { } sub create_submenu { - my ($link,$target,$title,$submenu,$translate,$addclass) = @_; + my ($link,$target,$title,$submenu,$translate,$addclass,$listclass,$linkattr) = @_; return unless (ref($submenu) eq 'ARRAY'); - my $disptarget; - if ($target ne '') { - $disptarget = ' target="'.$target.'"'; + my $targetattr; + if (($target ne '') && ($link ne '#')) { + $targetattr = ' target="'.$target.'"'; } my $menu = '
  • '. - ''. + ''. ''.$title. ''. ' ▼'. @@ -567,7 +758,7 @@ sub create_submenu { # $link and $title are only used in the initial string written in $menu # as seen above, not needed for nested submenus - $menu .= &build_submenu($target, $submenu, $translate, '1'); + $menu .= &build_submenu($target, $submenu, $translate, '1', $listclass, $linkattr); $menu .= '
  • '; return $menu; @@ -577,7 +768,7 @@ sub create_submenu { # build the dropdown (and nested submenus) recursively # see perldoc create_submenu documentation for further information sub build_submenu { - my ($target, $submenu, $translate, $first_level) = @_; + my ($target, $submenu, $translate, $first_level, $listclass, $linkattr) = @_; unless (@{$submenu}) { return ''; } @@ -621,13 +812,32 @@ sub build_submenu { next unless (($env{'user.name'} ne '') && ($env{'user.domain'} ne '')); $href =~ s/\[domain\]/$env{'user.domain'}/g; $href =~ s/\[user\]/$env{'user.name'}/g; + } elsif (($href =~ m{^/adm/preferences\?}) && ($href =~ /\[returnurl\]/)) { + my $returnurl = $ENV{'REQUEST_URI'}; + if ($ENV{'REQUEST_URI'} =~ m{/adm/preferences\?action=(?:changedomcoord|authorsettings)\&returnurl=([^\&]+)$}) { + $returnurl = $1; + } + if (($returnurl =~ m{^/adm/createuser($|\?action=)}) || + ($returnurl =~ m{^/priv/$match_domain/$match_username}) || + ($returnurl =~ m{^/res(/?$|/$match_domain/$match_username)})) { + $returnurl =~ s{\?.*$}{}; + $returnurl = '&returnurl='.&HTML::Entities::encode($returnurl,'"<>&\''); + } else { + undef($returnurl); + } + $href =~ s/\[returnurl\]/$returnurl/; } + my $targetattr; unless (($href eq '') || ($href =~ /^\#/)) { - $target = ' target="_top"'; + if ($target ne '') { + $targetattr = ' target="'.$target.'"'; + } } - $menu .= '
  • '; - $menu .= '' . $title . ''; + $menu .= '
  • '; + $menu .= '' . $title . ''; $menu .= '
  • '; } } @@ -636,7 +846,8 @@ sub build_submenu { } sub innerregister { - my ($forcereg,$bread_crumbs,$group,$pagebuttonshide,$hostname) = @_; + my ($forcereg,$bread_crumbs,$group,$pagebuttonshide,$hostname, + $ltiscope,$ltiuri,$showncrumbsref) = @_; my $const_space = ($env{'request.state'} eq 'construct'); my $is_const_dir = 0; @@ -665,23 +876,28 @@ sub innerregister { my $maptitle = &Apache::lonnet::gettitle($mapurl); my $restitle = &Apache::lonnet::gettitle($symb); my (@crumbs,@mapcrumbs); - if (($env{'request.noversionuri'} ne '/adm/navmaps') && ($mapurl ne '')) { - $navmap = Apache::lonnavmaps::navmap->new(); - if (ref($navmap)) { - @mapcrumbs = $navmap->recursed_crumbs($mapurl); + 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'}))) { + unless ((($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'}) { if (@mapcrumbs) { push(@crumbs,@mapcrumbs); - } else { + } elsif (!(($crstype eq 'Placement') && (!$env{'request.role.adv'})) && + ($ltiscope ne 'map') && ($ltiscope ne 'resource')) { push(@crumbs, {text => '...', no_mt => 1}); } @@ -689,11 +905,14 @@ sub innerregister { unless ((($crstype eq 'Placement') && (!$env{'request.role.adv'})) || (@mapcrumbs) || (!$maptitle) || ($maptitle eq 'default.sequence') || - ($mapurl eq $env{'course.'.$env{'request.course.id'}.'.url'})) { - push @crumbs, {text => $maptitle, no_mt => 1, href => $mapurl}; + ($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(); @@ -720,14 +939,17 @@ sub innerregister { if ($env{'form.folderpath'}) { &prepare_functions($resurl,$forcereg,$group,undef,undef,1,$hostname); ($trail) = - &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1); + &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1,1); } else { &Apache::lonhtmlcommon::add_breadcrumb( {text => "Supplemental $crstype Content", href => "javascript:gopost('/adm/supplemental','')"}); $title = &mt('View Resource'); ($trail) = - &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1); + &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1,1); + } + if (ref($showncrumbsref)) { + $$showncrumbsref = 1; } return $trail; } elsif ($resurl =~ m{^\Q/uploaded$courseurl/portfolio/syllabus/}) { @@ -736,7 +958,10 @@ sub innerregister { $forcereg,$group,undef,undef,1,$hostname); $title = &mt('Syllabus File'); my ($trail) = - &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1,$hostname); + &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1,1); + if (ref($showncrumbsref)) { + $$showncrumbsref = 1; + } return $trail; } unless ($env{'request.state'} eq 'construct') { @@ -775,8 +1000,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')); @@ -791,8 +1022,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')", @@ -852,6 +1083,13 @@ sub innerregister { $currdir =~ s|[^/]+$||; my $cleandisfn = &Apache::loncommon::escape_single($thisdisfn); my $esc_currdir = &Apache::loncommon::escape_single($currdir); + my $pubfile = "/res/$udom/$uname/$thisdisfn"; + my $candelete = 1; + if (-e $londocroot.$pubfile) { + unless (&Apache::lonnet::metadata($pubfile,'obsolete')) { + undef($candelete); + } + } # # Probably should be in mydesk.tab # @@ -859,7 +1097,18 @@ sub innerregister { s&6&1&list.png&Directory&dir[_1]&golist('$esc_currdir')&List current directory s&6&2&rtrv.png&Retrieve&version[_1]&gocstr('/adm/retrieve','/priv/$udom/$uname/$cleandisfn')&Retrieve old version s&6&3&pub.png&Publish&resource[_3]&gocstr('/adm/publish','/priv/$udom/$uname/$cleandisfn')&Publish this resource +s&7&3©.png&Copy&resource[_4]&gocstr('/adm/cfile?action=copy','/priv/$udom/$uname/$cleandisfn')&Copy this resource +ENDMENUITEMS +# +# Rename and Delete only available if obsolete or unpublished +# + if ($candelete) { + $menuitems .= (<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"; @@ -934,14 +1211,17 @@ if ($env{'browser.mobile'}) { $is_mobile = 1; } - unless ($env{'request.noversionuri'}=~/\/(bulletinboard|smppg|navmaps|syllabus|aboutme|viewclasslist|portfolio|ext\.tool)(\?|$)/) { - 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/|default_\d+\.page$)}) && + ($env{'request.noversionuri'} !~ m{^/adm/.+/ext\.tool$})) { $menuitems.=(< 0) { @@ -1290,8 +1626,13 @@ sub advtools_crumbs { &Apache::lonhtmlcommon::add_breadcrumb_tool( 'advtools', @funcs[61,64,65,66,67,74]); } elsif ($env{'request.noversionuri'} !~ m{^/adm/(navmaps|viewclasslist)(\?|$)}) { - &Apache::lonhtmlcommon::add_breadcrumb_tool( - 'advtools', @funcs[61,71,72,73,74,92]); + if ($env{'request.state'} eq 'construct') { + &Apache::lonhtmlcommon::add_breadcrumb_tool( + 'advtools', @funcs[61,73,74,71,72]); + } else { + &Apache::lonhtmlcommon::add_breadcrumb_tool( + 'advtools', @funcs[61,71,72,73,74,92]); + } } elsif ($env{'request.noversionuri'} eq '/adm/viewclasslist') { &Apache::lonhtmlcommon::add_breadcrumb_tool( 'advtools', $funcs[61]); @@ -1355,7 +1696,8 @@ sub switch { unless ($env{'request.state'} eq 'construct') { push(@tools,63); } - if (($env{'environment.icons'} eq 'iconsonly') && + if ((($env{'environment.icons'} eq 'iconsonly') || + ($env{'environment.icons'} eq '') && ($env{'request.lti.login'})) && (grep(/^$idx$/,@tools))) { $inlineremote[$idx] = ''.$pic.''; @@ -1626,8 +1968,17 @@ sub check_for_rcrs { if (!$showreqcrs) { foreach my $type (@reqtypes) { if ($env{'environment.reqcrsotherdom.'.$type} ne '') { - $showreqcrs = 1; - last; + my @domains = split(',',$env{'environment.reqcrsotherdom.'.$type}); + foreach my $entry (@domains) { + my ($extdom,$extopt) = split(':',$entry); + if (&Apache::lonnet::will_trust('reqcrs',$env{'user.domain'},$extdom)) { + $showreqcrs = 1; + last; + } + } + if ($showreqcrs) { + last; + } } } } @@ -1706,13 +2057,18 @@ sub done_button_js { key => 'Key:', nokey => 'A proctor key is required', ); + my $shownsymb = &HTML::Entities::encode(&Apache::lonenc::check_encrypt($env{'request.symb'})); my $navmap = Apache::lonnavmaps::navmap->new(); my ($missing,$tried) = (0,0); if (ref($navmap)) { my @resources=(); if ($type eq 'map') { my ($mapurl,$rid,$resurl)=&Apache::lonnet::decode_symb($env{'request.symb'}); - @resources=$navmap->retrieveResources($mapurl,sub { $_[0]->is_problem() }); + if ($env{'request.symb'} =~ /\.page$/) { + @resources=$navmap->retrieveResources($resurl,sub { $_[0]->is_problem() }); + } else { + @resources=$navmap->retrieveResources($mapurl,sub { $_[0]->is_problem() }); + } } else { my $res = $navmap->getBySymb($env{'request.symb'}); if (ref($res)) { @@ -1764,6 +2120,7 @@ sub done_button_js {
    +
    @@ -1845,6 +2202,7 @@ END
    +
    @@ -1890,13 +2248,17 @@ END sub utilityfunctions { my ($httphost) = @_; my $currenturl=&Apache::lonnet::clutter(&Apache::lonnet::fixversion((split(/\?/,$env{'request.noversionuri'}))[0])); - if ($currenturl =~ m{^/adm/wrapper/ext/} - && $env{'request.external.querystring'} ) { + my $currentsymb=&Apache::lonenc::check_encrypt($env{'request.symb'}); + if ($currenturl =~ m{^/adm/wrapper/ext/}) { + if ($env{'request.external.querystring'}) { $currenturl .= ($currenturl=~/\?/)?'&':'?'.$env{'request.external.querystring'}; + } + my ($anchor) = ($env{'request.symb'} =~ /(\#[^\#]+)$/); + if (($anchor) && ($currenturl !~ /\Q$anchor\E$/)) { + $currenturl .= $1; + } } $currenturl=&Apache::lonenc::check_encrypt(&unescape($currenturl)); - - my $currentsymb=&Apache::lonenc::check_encrypt($env{'request.symb'}); my $dc_popup_cid; if ($env{'user.adv'} && exists($env{'user.role.dc./'. @@ -1920,9 +2282,22 @@ sub utilityfunctions { my $esc_url=&escape($currenturl); my $esc_symb=&escape($currentsymb); + my $newname = &mt('New Name'); my $countdown = &countdown_toggle_js(); + my ($ltitarget,$deeplinktarget); + if ($env{'request.lti.login'}) { + $ltitarget = $env{'request.lti.target'}; + } + if ($env{'request.deeplink.login'}) { + $deeplinktarget = $env{'request.deeplink.target'}; + } + + my $annotateurl = '/adm/annotation'; + if ($httphost) { + $annotateurl = '/adm/annotations'; + } my $hostvar = ' function setLCHost() { var lcHostname=""; @@ -1981,6 +2356,22 @@ function gocstr(url,filename) { this.document.cstrdelete.submit(); return; } + if ((url == '/adm/cfile?action=copy') || (url == '/adm/cfile?action=rename')) { + this.document.cstrcopy.filename.value = filename; + var oldname = filename.substring(filename.lastIndexOf("/") + 1); + var newname=prompt('$newname',oldname); + if (newname == "" || !newname || newname == oldname) { + return; + } + if (url == '/adm/cfile?action=rename') { + this.document.cstrcopy.action.value = 'rename'; + } else { + this.document.cstrcopy.action.value = 'copy'; + } + this.document.cstrcopy.newfilename.value = newname; + this.document.cstrcopy.submit(); + return; + } if (url == '/adm/printout') { this.document.cstrprint.postdata.value = filename this.document.cstrprint.curseed.value = 0; @@ -2019,17 +2410,23 @@ function golist(url) { currentURL = null; currentSymb= null; var lcHostname = setLCHost(); - top.location.href=lcHostname+url; + var ltitarget = '$ltitarget'; + var deeplinktarget = '$deeplinktarget'; + if ((ltitarget == 'iframe') || (deeplinktarget == '_self')) { + document.location.href=lcHostname+url; + } else { + top.location.href=lcHostname+url; + } } } -function catalog_info(isMobile) { +function catalog_info(url,isMobile) { if (isMobile == 1) { - openMyModal(window.location.pathname+'.meta?modal=1',500,400,'yes'); + openMyModal(url+'.meta?modal=1',500,400,'yes'); } else { - loncatinfo=window.open(window.location.pathname+'.meta',"LONcatInfo",'height=500,width=400,resizable=yes,scrollbars=yes,location=no,menubar=no,toolbar=no'); + loncatinfo=window.open(url+'.meta',"LONcatInfo",'height=500,width=400,resizable=yes,scrollbars=yes,location=no,menubar=no,toolbar=no'); } } @@ -2051,7 +2448,7 @@ function annotate() { annotator.document.write( '$start_page_annotate' +"
    " + +"action='$annotateurl'>" +"" +"<\\/form>" +'$end_page_annotate'); @@ -2072,6 +2469,23 @@ function open_StoredLinks_Import(rat) { newWin.focus(); } +function open_source() { + sourcewin=window.open('/adm/source?inhibitmenu=yes&viewonly=1&filename='+currentURL,'LONsource', + 'height=500,width=600,resizable=yes,location=no,menubar=no,toolbar=no,scrollbars=yes'); +} + +function open_aboutLC() { + var isMobile = "$env{'browser.mobile'}"; + var url = '/adm/about.html'; + if (isMobile == 1) { + openMyModal(url,600,400,'yes'); + } else { + window.open(url,"aboutLONCAPA","height=400,width=600,scrollbars=1,resizable=1,menubar=0,location=1"); + } + return; +} + + (function (\$) { \$(document).ready(function () { \$.single=function(a){return function(b){a[0]=b;return a}}(\$([1])); @@ -2092,8 +2506,15 @@ ENDUTILITY } sub serverform { + my $target; + unless (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) { + $target = ' target="_top"'; + } + if (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'} eq '_self')) { + $target = ' target="_self"'; + } return(< + @@ -2103,19 +2524,36 @@ ENDSERVERFORM } sub constspaceform { + my ($frameset) = @_; + my ($target,$printtarget); + if ($frameset) { + $target = ' target="_parent"'; + $printtarget = ' target="_parent"'; + } else { + unless ((($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) || + (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'} eq '_self'))) { + $target = ' target="_top"'; + $printtarget = ' target="_top"'; + } + } return(< + -
    +
    -
    +
    +
    + + + +
    ENDCONSTSPACEFORM } @@ -2131,12 +2569,12 @@ sub hidden_button_check { } sub roles_selector { - my ($cdom,$cnum,$httphost) = @_; + my ($cdom,$cnum,$httphost,$target,$menucoll,$menuref) = @_; my $crstype = &Apache::loncommon::course_type(); my $now = time; my (%courseroles,%seccount,%courseprivs,%roledesc); my $is_cc; - my ($js,$form,$switcher); + my ($js,$form,$switcher,$has_opa_priv); my $ccrole; if ($crstype eq 'Community') { $ccrole = 'co'; @@ -2237,10 +2675,15 @@ sub roles_selector { } } if ((keys(%seccount) > 1) || ($numdiffsec > 1)) { + my $targetattr; + if ($target ne '') { + $targetattr = ' target="'.$target.'"'; + } my @submenu; - $js = &jump_to_role($cdom,$cnum,\%seccount,\%courseroles,\%courseprivs,\%roledesc,$privref); + $js = &jump_to_role($cdom,$cnum,\%seccount,\%courseroles,\%courseprivs, + \%roledesc,$privref,$menucoll,$menuref); $form = - '
    '."\n". + ''."\n". ' '."\n". ' '."\n". @@ -2257,12 +2700,19 @@ sub roles_selector { if ($env{'request.role'} =~ m{^\Q$role\E}) { if ($seccount{$role} > 1) { $include = 1; + } else { + if ($env{'user.priv.'.$env{'request.role'}."./$cdom/$cnum"} =~/opa\&([^\:]*)/) { + $has_opa_priv = 1; + } } } else { $include = 1; } } if ($include) { + if ($env{"user.priv.$role./$cdom/$cnum./$cdom/$cnum"} =~/opa\&([^\:]*)/) { + $has_opa_priv = 1; + } push(@submenu,['javascript:adhocRole('."'$role'".')', &Apache::lonnet::plaintext($role,$crstype)]); } @@ -2287,16 +2737,19 @@ sub roles_selector { } else { $rolename = &Apache::lonnet::plaintext($role); } + if ($env{"user.priv.$role./$cdom/$cnum./$cdom/$cnum"} =~/opa\&([^\:]*)/) { + $has_opa_priv = 1; + } push(@submenu,['javascript:adhocRole('."'$role'".')', $rolename]); } } } if (@submenu > 0) { - $switcher = &create_submenu('','',&mt('Switch role'),\@submenu); + $switcher = &create_submenu('#',$target,&mt('Switch role'),\@submenu); } } - return ($js,$form,$switcher); + return ($js,$form,$switcher,$has_opa_priv); } sub get_all_courseroles { @@ -2463,7 +2916,8 @@ sub get_customadhoc_roles { } sub jump_to_role { - my ($cdom,$cnum,$seccount,$courseroles,$courseprivs,$roledesc,$privref) = @_; + my ($cdom,$cnum,$seccount,$courseroles,$courseprivs,$roledesc,$privref, + $menucoll,$menuref) = @_; my %lt = &Apache::lonlocal::texthash( this => 'This role has section(s) associated with it.', ente => 'Enter a specific section.', @@ -2474,6 +2928,7 @@ sub jump_to_role { role => 'The role you selected is not permitted to view the current page.', swit => 'Switch role, but display Main Menu page instead?', ); + &js_escape(\%lt); my $js; if (ref($courseroles) eq 'HASH') { $js = ' var secpick = new Array("'.$lt{'ente'}.'","'.$lt{'orlb'}.'");'."\n". @@ -2496,6 +2951,8 @@ sub jump_to_role { } } my $checkroles = 0; + my $fallback = '/adm/menu'; + my $displaymsg = $lt{'swit'}; if ((ref($privref) eq 'ARRAY') && (@{$privref} > 0) && (ref($courseprivs) eq 'HASH')) { my %disallowed; foreach my $role (sort(keys(%{$courseprivs}))) { @@ -2517,8 +2974,22 @@ sub jump_to_role { $checkroles = 1; $js .= " var disallow = new Array('".join("','",keys(%disallowed))."');\n". " var rolecheck = 1;\n"; + if ($menucoll) { + if (ref($menuref) eq 'HASH') { + if ($menuref->{'main'} eq 'n') { + $fallback = '/adm/navmaps'; + if (&Apache::loncommon::course_type() eq 'Community') { + $displaymsg = &mt('Switch role, but display Community Contents page instead?'); + } else { + $displaymsg = &mt('Switch role, but display Course Contents page instead?'); + } + &js_escape(\$displaymsg); + } + } + } } } + &js_escape(\$fallback); if (!$checkroles) { $js .= " var disallow = new Array();\n". " rolecheck = 0;\n"; @@ -2541,8 +3012,8 @@ function adhocRole(newrole) { if (rolecheck > 0) { for (var i=0; i{'starttime'} < $now) && @@ -2718,6 +3199,86 @@ sub placement_progress { &mt('Test is [_1]% complete',$complete).''; } +sub linkprot_exit { + if (($env{'request.ciurse.id'}) && ($env{'request.deeplink.login'})) { + my ($deeplink_symb,$deeplink); + my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + if (($cnum ne '') && ($cdom ne '')) { + $deeplink_symb = &Apache::loncommon::deeplink_login_symb($cnum,$cdom); + if ($deeplink_symb) { + if ($deeplink_symb =~ /\.(page|sequence)$/) { + my $mapname = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($deeplink_symb))[2]); + my $navmap = Apache::lonnavmaps::navmap->new(); + if (ref($navmap)) { + $deeplink = $navmap->get_mapparam(undef,$mapname,'0.deeplink'); + } + } else { + $deeplink = &Apache::lonnet::EXT('resource.0.deeplink',$deeplink_symb); + } + if ($deeplink ne '') { + my ($state,$others,$listed,$scope,$protect,$display,$target,$exit) = split(/,/,$deeplink); + my %lt = &Apache::lonlocal::texthash( + title => 'Exit Tool', + okdone => 'Click "OK" to exit embedded tool', + cancel => 'Click "Cancel" to continue working.', + ok => 'OK', + exit => 'Cancel', + ); + if ($exit) { + my $height = 250; + my $width = 300; + my $exitbuttontext = &mt('Exit Tool'); + return < + + + + +
    +

    $lt{'okdone'} $lt{'cancel'}

    +
    + + + +END + } + } + } + } + } + return; +} + # ================================================================ Main Program BEGIN { @@ -2734,14 +3295,17 @@ BEGIN { $category_positions{$entries[2]}=$entries[1]; $category_names{$entries[2]}=$entries[3]; } elsif ($configline=~/^prim\:/) { - my @entries = (split(/\:/, $configline))[1..6]; + my @entries = (split(/\:/, $configline))[1..7]; push(@primary_menu,\@entries); } elsif ($configline=~/^primsub\:/) { - my ($parent,@entries) = (split(/\:/, $configline))[1..4]; + my ($parent,@entries) = (split(/\:/, $configline))[1..5]; push(@{$primary_submenu{$parent}},\@entries); } elsif ($configline=~/^scnd\:/) { - my @entries = (split(/\:/, $configline))[1..5]; + my @entries = (split(/\:/, $configline))[1..6]; push(@secondary_menu,\@entries); + } elsif ($configline=~/^scndsub\:/) { + my ($parent,@entries) = (split(/\:/, $configline))[1..4]; + push(@{$secondary_submenu{$parent}},\@entries); } elsif ($configline) { push(@desklines,$configline); } 500 Internal Server Error

    Internal Server Error

    The server encountered an internal error or misconfiguration and was unable to complete your request.

    Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

    More information about this error may be available in the server error log.