--- loncom/interface/lonmenu.pm 2011/01/16 22:45:05 1.342
+++ loncom/interface/lonmenu.pm 2016/03/15 14:25:26 1.440
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Routines to control the menu
#
-# $Id: lonmenu.pm,v 1.342 2011/01/16 22:45:05 www Exp $
+# $Id: lonmenu.pm,v 1.440 2016/03/15 14:25:26 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -26,8 +26,6 @@
# http://www.lon-capa.org/
#
#
-# There is one parameter controlling the action of this module:
-#
=head1 NAME
@@ -35,7 +33,8 @@ Apache::lonmenu
=head1 SYNOPSIS
-Coordinates the response to clicking an image.
+Loads contents of /home/httpd/lonTabs/mydesk.tab,
+used to generate inline menu, and Main Menu page.
This is part of the LearningOnline Network with CAPA project
described at http://www.lon-capa.org.
@@ -74,10 +73,19 @@ It is set to 'done' in the BEGIN block o
=item @primary_menu
The elements of this array reference arrays that are made up of the components
-of those lines of mydesk.tab that start with prim.
+of those lines of mydesk.tab that start with prim:.
It is used by primary_menu() to generate the corresponding menu.
It gets filled in the BEGIN block of this module.
+=item %primary_sub_menu
+
+The keys of this hash reference are the names of items in the primary_menu array
+which have sub-menus. For each key, the corresponding value is a reference to
+an array containing components extracted from lines in mydesk.tab which begin
+with primsub:.
+This hash, which is used by primary_menu to generate sub-menus, is populated in
+the BEGIN block.
+
=item @secondary_menu
The elements of this array reference arrays that are made up of the components
@@ -98,15 +106,55 @@ secondary_menu().
=item primary_menu()
-This routine evaluates @primary_menu and returns XHTML for the menu
-that contains following links: About, Message, Roles, Help, Logout
+This routine evaluates @primary_menu and returns a two item array,
+with the array elements containing XHTML for the left and right sides of
+the menu that contains the following links: About, Message, Roles, Help, Logout
@primary_menu is filled within the BEGIN block of this module with
-entries from mydesk.tab
+entries from mydesk.tab
=item secondary_menu()
Same as primary_menu() but operates on @secondary_menu.
+=item create_submenu()
+
+Creates XHTML for unordered list of sub-menu items which belong to a
+particular top-level menu item. Uses hover pseudo class in css to display
+dropdown list when mouse hovers over top-level item. Support for IE6
+(no hover psuedo class) via LC_hoverable class for
tag for top-
+level item, which employs jQuery to handle behavior on mouseover.
+
+Inputs: 4 - (a) link and (b) target for anchor href in top level item,
+ (c) title for text wrapped by anchor tag in top level item.
+ (d) reference to array of arrays of sub-menu items.
+
+ The underlying datastructure used in (d) contains data from mydesk.tab.
+ It consists of an array which has an array for each item appearing in
+ the menu (e.g. [["link", "title", "condition"]] for a single-item menu).
+ create_submenu() supports also the creation of XHTML for nested dropdown
+ menus represented by unordered lists. This is done by replacing the
+ scalar used for the link with an arrayreference containing the menuitems
+ for the nested menu. This can be done recursively so that the next menu
+ may also contain nested submenus.
+
+ Example:
+ [ # begin of datastructure
+ ["/home/", "Home", "condition1"], # 1st item of the 1st layer menu
+ [ # 2nd item of the 1st layer menu
+ [ # anon. array for nested menu
+ ["/path1", "Path1", undef], # 1st item of the 2nd layer menu
+ ["/path2", "Path2", undef], # 2nd item of the 2nd layer menu
+ [ # 3rd item of the 2nd layer menu
+ [[...], [...], ..., [...]], # containing another menu layer
+ "Sub-Sub-Menu", # title for this container
+ undef
+ ]
+ ], # end of array/nested menu
+ "Sub-Menu", # title for the container item
+ undef
+ ] # end of 2nd item of the 1st layer menu
+]
+
=item innerregister()
This gets called in order to register a URL in the body of the document
@@ -129,6 +177,9 @@ The javascript is usually similar to "go
=item utilityfunctions()
+Output from this routine is a number of javascript functions called by
+items in the inline menu, and in some cases items in the Main Menu page.
+
=item serverform()
=item constspaceform()
@@ -153,11 +204,13 @@ use Apache::lonhtmlcommon();
use Apache::loncommon();
use Apache::lonenc();
use Apache::lonlocal;
+use Apache::lonmsg();
use LONCAPA qw(:DEFAULT :match);
use HTML::Entities();
+use Apache::lonwishlist();
use vars qw(@desklines %category_names %category_members %category_positions
- $readdesk @primary_menu @secondary_menu);
+ $readdesk @primary_menu %primary_submenu @secondary_menu);
my @inlineremote;
@@ -178,15 +231,16 @@ sub prep_menuitem {
. qq| href="$$menuitem[0]" target="_top">$link
|;
}
-# primary_menu() evaluates @primary_menu and returns XHTML for the menu
-# that contains following links:
-# About, Message, Roles, Help, Logout
+# primary_menu() evaluates @primary_menu and returns a two item array,
+# with the array elements containing XHTML for the left and right sides of
+# the menu that contains the following links:
+# Personal, About, Message, Roles, Help, Logout
# @primary_menu is filled within the BEGIN block of this module with
# entries from mydesk.tab
sub primary_menu {
- my $menu;
+ my (%menu);
# each element of @primary contains following array:
- # (link url, icon path, alt text, link text, condition)
+ # (link url, icon path, alt text, link text, condition, position)
my $public;
if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public'))
|| (($env{'user.name'} eq '') && ($env{'user.domain'} eq ''))) {
@@ -210,8 +264,41 @@ sub primary_menu {
next if $$menuitem[4] eq 'courses' ##'Roles' wanted
&& !&Apache::loncommon::show_course(); ##
-
- if ($$menuitem[3] eq 'Help') { # special treatment for helplink
+ my $title = $menuitem->[3];
+ my $position = $menuitem->[5];
+ if ($position eq '') {
+ $position = 'right';
+ }
+ if (defined($primary_submenu{$title})) {
+ my ($link,$target);
+ if ($menuitem->[0] ne '') {
+ $link = $menuitem->[0];
+ $target = '_top';
+ } else {
+ $link = '#';
+ }
+ my @primsub;
+ if (ref($primary_submenu{$title}) eq 'ARRAY') {
+ foreach my $item (@{$primary_submenu{$title}}) {
+ next if (($item->[2] eq 'wishlist') && (!$env{'user.adv'}));
+ next if ((($item->[2] eq 'portfolio') ||
+ ($item->[2] eq 'blog')) &&
+ (!&Apache::lonnet::usertools_access('','',$item->[2],
+ undef,'tools')));
+ push(@primsub,$item);
+ }
+ if (@primsub > 0) {
+ if ($title eq 'Personal' && $env{'user.name'} && $env{'user.domain'} ) {
+ $title = &Apache::loncommon::plainname($env{'user.name'},$env{'user.domain'});
+ } else {
+ $title = &mt($title);
+ }
+ $menu{$position} .= &create_submenu($link,$target,$title,\@primsub,1);
+ } elsif ($link) {
+ $menu{$position} .= '
';
+
+ # $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 .= '
';
+
+ return $menu;
+}
+
+# helper routine for 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) = @_;
+ if (!defined(@{$submenu})) {
+ return '';
+ }
+
+ my $menu = '';
+ my $count = 0;
+ my $numsub = scalar(@{$submenu});
+ foreach my $item (@{$submenu}) {
+ $count ++;
+ if (ref($item) eq 'ARRAY') {
+ my $href = $item->[0];
+ my $bordertop;
+ my $borderbot;
+ my $title;
+
+ if ($translate) {
+ $title = &mt($item->[1]);
+ } else {
+ $title = $item->[1];
+ }
+
+ if ($count == 1 && !$first_level) {
+ $bordertop = 'border-top: 1px solid black;';
+ }
+ if ($count == $numsub) {
+ $borderbot = 'border-bottom: 1px solid black;';
+ }
+
+ # href is a reference to another submenu
+ if (ref($href) eq 'ARRAY') {
+ $menu .= '
';
+ } else { # href is the actual hyperlink and does not represent another submenu
+ # for the current menu title
+ if ($href =~ /(aboutme|rss\.html)$/) {
+ next unless (($env{'user.name'} ne '') && ($env{'user.domain'} ne ''));
+ $href =~ s/\[domain\]/$env{'user.domain'}/g;
+ $href =~ s/\[user\]/$env{'user.name'}/g;
+ }
+ unless (($href eq '') || ($href =~ /^\#/)) {
+ $target = ' target="_top"';
+ }
- return "
+
+
+
+END
+ }
+ } else {
+ return;
+ }
+}
+
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'} ) {
@@ -1095,84 +1752,45 @@ sub utilityfunctions {
my $end_page_annotate =
&Apache::loncommon::end_page({'js_ready' => 1});
- my $confirm_switch = &mt("Editing requires switching to the resource's home server.").'\n'.
- &mt('Switch server?');
+ my $jumptores = &Apache::lonhtmlcommon::javascript_jumpto_resource();
- my $start_page_wishlistlink =
- &Apache::loncommon::start_page('Set link to wishlist',undef,
- {'only_body' => 1,
- 'js_ready' => 1,
- 'bgcolor' => '#FFFFFF',});
+ my $esc_url=&escape($currenturl);
+ my $esc_symb=&escape($currentsymb);
- my $warningLink = &mt('You must insert a title!');
+ my $countdown = &countdown_toggle_js();
- # HTML-Markup for 'Set a link for this resource to wishlist'
- # this is written via JavaScript document.write (function set_wishlistlink)
- # it is split into 3 parts and the inputfields for title and path are left out
- # these fields are inserted later to set the values for title and path
- # automatically via JavaScript (document.title and location.pathname)
- my %folders = &Apache::lonnet::get('wishlist',['folders']);
- if ($folders{'folders'} eq '') {
- $folders{'folders'} = '';
- }
- my $in_page_wishlistlink1 = '
'.&mt('Set a link to wishlist').'
'.
- '';
-
- # remove all \n for inserting on javascript document.write
- $in_page_wishlistlink1 =~ s/\n//g;
- $in_page_wishlistlink2 =~ s/\n//g;
- $in_page_wishlistlink3 =~ s/\n//g;
-
- my $end_page_wishlistlink =
- &Apache::loncommon::end_page({'js_ready' => 1});
+ my $hostvar = '
+function setLCHost() {
+ var lcHostname="";
+';
+ if ($httphost =~ m{^https?\://}) {
+ $hostvar .= ' var lcServer="'.$httphost.'";'."\n".
+ ' var hostReg = /^https?:\/\/([^\/]+)$/i;'."\n".
+ ' var match = hostReg.exec(lcServer);'."\n".
+ ' if (match.length) {'."\n".
+ ' if (match[1] == location.hostname) {'."\n".
+ ' lcHostname=lcServer;'."\n".
+ ' }'."\n".
+ ' }'."\n";
+ }
+
+ $hostvar .= ' return lcHostname;'."\n".
+'}'."\n";
return (<'
- +'function newlinksubmit(){'
- +'var title = document.getElementsByName("title")[0].value;'
- +'if (!title) {'
- +'alert("$warningLink");'
- +'return false;}'
- +'return true;}'
- +'<\/scr'+'ipt>'
- +'$in_page_wishlistlink1'
- +''
- +'$in_page_wishlistlink2'
- +''
- +'$in_page_wishlistlink3'
- +'$end_page_wishlistlink' );
- wishlistlink.document.close();
-}
-
-function open_Wishlist_Import(rat) {
+function open_StoredLinks_Import(rat) {
var newWin;
+ var lcHostname = setLCHost();
if (rat) {
- newWin = window.open('/adm/wishlist?inhibitmenu=yes&mode=import&rat='+rat,
+ newWin = window.open(lcHostname+'/adm/wishlist?inhibitmenu=yes&mode=import&rat='+rat,
'wishlistImport','scrollbars=1,resizable=1,menubar=0');
}
else {
- newWin = window.open('/adm/wishlist?inhibitmenu=yes&mode=import',
+ newWin = window.open(lcHostname+'/adm/wishlist?inhibitmenu=yes&mode=import',
'wishlistImport','scrollbars=1,resizable=1,menubar=0');
}
newWin.focus();
}
+(function (\$) {
+ \$(document).ready(function () {
+ \$.single=function(a){return function(b){a[0]=b;return a}}(\$([1]));
+ /*\@cc_on
+ if (!window.XMLHttpRequest) {
+ \$('.LC_hoverable').each(function () {
+ this.attachEvent('onmouseenter', function (evt) { \$.single(evt.srcElement).addClass('hover'); });
+ this.attachEvent('onmouseleave', function (evt) { \$.single(evt.srcElement).removeClass('hover'); });
+ });
+ }
+ \@*/
+ });
+}(jQuery));
+
+$countdown
+
ENDUTILITY
}
@@ -1353,18 +1967,42 @@ sub hidden_button_check {
}
sub roles_selector {
- my ($cdom,$cnum) = @_;
+ my ($cdom,$cnum,$httphost) = @_;
my $crstype = &Apache::loncommon::course_type();
my $now = time;
- my (%courseroles,%seccount);
+ my (%courseroles,%seccount,%courseprivs);
my $is_cc;
- my $role_selector;
+ my ($js,$form,$switcher);
my $ccrole;
if ($crstype eq 'Community') {
$ccrole = 'co';
} else {
$ccrole = 'cc';
- }
+ }
+ my ($priv,$gotsymb,$destsymb);
+ my $destinationurl = $ENV{'REQUEST_URI'};
+ if ($destinationurl =~ /\?symb=/) {
+ $gotsymb = 1;
+ } elsif ($destinationurl =~ m{^/enc/}) {
+ my $plainurl = &Apache::lonenc::unencrypted($destinationurl);
+ if ($plainurl =~ /\?symb=/) {
+ $gotsymb = 1;
+ }
+ }
+ unless ($gotsymb) {
+ $destsymb = &Apache::lonnet::symbread();
+ if ($destsymb ne '') {
+ $destsymb = &Apache::lonenc::check_encrypt($destsymb);
+ }
+ }
+ my $reqprivs = &required_privs();
+ if (ref($reqprivs) eq 'HASH') {
+ my $destination = $destinationurl;
+ $destination =~ s/(\?.*)$//;
+ if (exists($reqprivs->{$destination})) {
+ $priv = $reqprivs->{$destination};
+ }
+ }
if ($env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum}) {
my ($start,$end) = split(/\./,$env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum});
@@ -1377,7 +2015,7 @@ sub roles_selector {
}
}
if ($is_cc) {
- &get_all_courseroles($cdom,$cnum,\%courseroles,\%seccount);
+ &get_all_courseroles($cdom,$cnum,\%courseroles,\%seccount,\%courseprivs,$priv);
} else {
my %gotnosection;
foreach my $item (keys(%env)) {
@@ -1393,6 +2031,18 @@ sub roles_selector {
$gotnosection{$role} = 1;
}
}
+ if ($priv ne '') {
+ my $cnumsec = $cnum;
+ if ($sec ne '') {
+ $cnumsec .= "/$sec";
+ }
+ $courseprivs{"$role./$cdom/$cnumsec./"} =
+ $env{"user.priv.$role./$cdom/$cnumsec./"};
+ $courseprivs{"$role./$cdom/$cnumsec./$cdom/"} =
+ $env{"user.priv.$role./$cdom/$cnumsec./$cdom/"};
+ $courseprivs{"$role./$cdom/$cnumsec./$cdom/$cnumsec"} =
+ $env{"user.priv.$role./$cdom/$cnumsec./$cdom/$cnumsec"};
+ }
if (ref($courseroles{$role}) eq 'ARRAY') {
if ($sec ne '') {
if (!grep(/^\Q$sec\E$/,@{$courseroles{$role}})) {
@@ -1410,42 +2060,71 @@ sub roles_selector {
}
}
}
- my $switchtext;
- if ($crstype eq 'Community') {
- $switchtext = &mt('Switch community role to...')
- } else {
- $switchtext = &mt('Switch course role to...')
- }
my @roles_order = ($ccrole,'in','ta','ep','ad','st');
- if (keys(%courseroles) > 1) {
- $role_selector = &jump_to_role($cdom,$cnum,\%seccount,\%courseroles);
- $role_selector .= '';
+ if (@submenu > 0) {
+ $switcher = &create_submenu('','',&mt('Switch role'),\@submenu);
+ }
}
- return $role_selector;
+ return ($js,$form,$switcher);
}
sub get_all_courseroles {
- my ($cdom,$cnum,$courseroles,$seccount) = @_;
- unless ((ref($courseroles) eq 'HASH') && (ref($seccount) eq 'HASH')) {
+ my ($cdom,$cnum,$courseroles,$seccount,$courseprivs) = @_;
+ unless ((ref($courseroles) eq 'HASH') && (ref($seccount) eq 'HASH') &&
+ (ref($courseprivs) eq 'HASH')) {
return;
}
my ($result,$cached) =
@@ -1453,9 +2132,11 @@ sub get_all_courseroles {
if (defined($cached)) {
if (ref($result) eq 'HASH') {
if ((ref($result->{'roles'}) eq 'HASH') &&
- (ref($result->{'seccount'}) eq 'HASH')) {
+ (ref($result->{'seccount'}) eq 'HASH') &&
+ (ref($result->{'privs'}) eq 'HASH')) {
%{$courseroles} = %{$result->{'roles'}};
%{$seccount} = %{$result->{'seccount'}};
+ %{$courseprivs} = %{$result->{'privs'}};
return;
}
}
@@ -1483,30 +2164,44 @@ sub get_all_courseroles {
push(@{$courseroles->{$urole}},$usec);
}
}
+ my $area = '/'.$cdom.'/'.$cnum;
+ if ($usec ne '') {
+ $area .= '/'.$usec;
+ }
+ if ($role =~ /^cr\//) {
+ &Apache::lonnet::custom_roleprivs($courseprivs,$urole,$cdom,$cnum,$urole.'.'.$area,$area);
+ } else {
+ &Apache::lonnet::standard_roleprivs($courseprivs,$urole,$cdom,$urole.'.'.$area,$cnum,$area);
+ }
}
my %sections_count = &Apache::loncommon::get_sections($cdom,$cnum,['st']);
@{$courseroles->{'st'}} = ();
+ &Apache::lonnet::standard_roleprivs($courseprivs,'st',$cdom,"st./$cdom/$cnum",$cnum,"/$cdom/$cnum");
if (keys(%sections_count) > 0) {
push(@{$courseroles->{'st'}},keys(%sections_count));
- $seccount->{'st'} = scalar(keys(%sections_count));
+ $seccount->{'st'} = scalar(keys(%sections_count));
}
+ $seccount->{'st'} ++; # Increment for a section-less student role.
my $rolehash = {
'roles' => $courseroles,
'seccount' => $seccount,
+ 'privs' => $courseprivs,
};
&Apache::lonnet::do_cache_new('getcourseroles',$cdom.'_'.$cnum,$rolehash);
return;
}
sub jump_to_role {
- my ($cdom,$cnum,$seccount,$courseroles) = @_;
+ my ($cdom,$cnum,$seccount,$courseroles,$courseprivs,$priv) = @_;
my %lt = &Apache::lonlocal::texthash(
this => 'This role has section(s) associated with it.',
ente => 'Enter a specific section.',
orlb => 'Enter a specific section, or leave blank for no section.',
avai => 'Available sections are:',
youe => 'You entered an invalid section choice:',
- plst => 'Please try again',
+ plst => 'Please try again.',
+ role => 'The role you selected is not permitted to view the current page.',
+ swit => 'Switch role, but display Main Menu page instead?',
);
my $js;
if (ref($courseroles) eq 'HASH') {
@@ -1529,14 +2224,44 @@ sub jump_to_role {
' numsec['.$i.'] = "'.$seccount->{$items[$i]}.'";'."\n";
}
}
+ my $checkroles = 0;
+ if ($priv && ref($courseprivs) eq 'HASH') {
+ my (%disallowed,%allowed,@disallow);
+ foreach my $role (sort(keys(%{$courseprivs}))) {
+ my $trole;
+ if ($role =~ m{^(.+?)\Q./$cdom/$cnum\E}) {
+ $trole = $1;
+ }
+ if (($trole ne '') && ($trole ne 'cm')) {
+ if ($courseprivs->{$role} =~ /\Q:$priv\E($|:|\&\w+)/) {
+ $allowed{$trole} = 1;
+ } else {
+ $disallowed{$trole} = 1;
+ }
+ }
+ }
+ foreach my $trole (keys(%disallowed)) {
+ unless ($allowed{$trole}) {
+ push(@disallow,$trole);
+ }
+ }
+ if (@disallow > 0) {
+ $checkroles = 1;
+ $js .= " var disallow = new Array('".join("','",@disallow)."');\n".
+ " var rolecheck = 1;\n";
+ }
+ }
+ if (!$checkroles) {
+ $js .= " var disallow = new Array();\n".
+ " rolecheck = 0;\n";
+ }
return <<"END";