--- loncom/interface/lonmenu.pm 2013/07/03 16:44:20 1.417 +++ 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.417 2013/07/03 16:44:20 raeburn Exp $ +# $Id: lonmenu.pm,v 1.440 2016/03/15 14:25:26 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -128,6 +128,33 @@ Inputs: 4 - (a) link and (b) target for (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 @@ -261,7 +288,12 @@ sub primary_menu { push(@primsub,$item); } if (@primsub > 0) { - $menu{$position} .= &create_submenu($link,$target,$title,\@primsub); + 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} .= '
  • '.&mt($title).'
  • '; } @@ -283,8 +315,14 @@ sub primary_menu { $menu{$position} .= prep_menuitem($menuitem); } } - return ("
      $menu{'left'}
    ", - "
      $menu{'right'}
    "); + my @output = ('',''); + if ($menu{'left'} ne '') { + $output[0] = "
      $menu{'left'}
    "; + } + if ($menu{'right'} ne '') { + $output[1] = "
      $menu{'right'}
    "; + } + return @output; } #returns hashref {user=>'',dom=>''} containing: @@ -313,6 +351,7 @@ sub getauthor{ } sub secondary_menu { + my ($httphost) = @_; my $menu; my $crstype = &Apache::loncommon::course_type(); @@ -321,6 +360,9 @@ sub secondary_menu { : ''); my $canedit = &Apache::lonnet::allowed('mdc', $env{'request.course.id'}); my $canviewroster = $env{'course.'.$env{'request.course.id'}.'.student_classlist_view'}; + if ($canviewroster eq 'disabled') { + undef($canviewroster); + } my $canviewgrps = &Apache::lonnet::allowed('vcg', $crs_sec); my $canmodifyuser = &Apache::lonnet::allowed('cst', $crs_sec); my $canviewwnew = &Apache::lonnet::allowed('whn', $crs_sec); @@ -329,7 +371,8 @@ sub secondary_menu { my $canmgr = &Apache::lonnet::allowed('mgr', $crs_sec); my $author = &getauthor(); - my ($cdom,$cnum,$showsyllabus,$showfeeds,$showresv); + my ($cdom,$cnum,$showsyllabus,$showfeeds,$showresv,$grouptools); + $grouptools = 0; if ($env{'request.course.id'}) { $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; @@ -355,6 +398,17 @@ 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)) { + my @privs = split(/:/,$env{"user.priv.$env{'request.role'}./$cdom/$cnum/$group"}); + shift(@privs); + if (@privs) { + $grouptools ++; + } + } + } } my ($canmodifycoauthor); @@ -365,12 +419,6 @@ sub secondary_menu { $canmodifycoauthor = 1; } } - - my %groups = &Apache::lonnet::get_active_groups( - $env{'user.domain'}, $env{'user.name'}, - $env{'course.' . $env{'request.course.id'} . '.domain'}, - $env{'course.' . $env{'request.course.id'} . '.num'}); - my ($roleswitcher_js,$roleswitcher_form); foreach my $menuitem (@secondary_menu) { @@ -399,7 +447,7 @@ sub secondary_menu { && !$canmodpara; next if $$menuitem[4] =~ /showgroups$/ && !$canviewgrps - && !%groups; + && !$grouptools; next if $$menuitem[4] eq 'showsyllabus' && !$showsyllabus; next if $$menuitem[4] eq 'showfeeds' @@ -414,7 +462,8 @@ sub secondary_menu { ($roleswitcher_js,$roleswitcher_form,my $switcher) = &roles_selector( $env{'course.' . $env{'request.course.id'} . '.domain'}, - $env{'course.' . $env{'request.course.id'} . '.num'} + $env{'course.' . $env{'request.course.id'} . '.num'}, + $httphost ); $menu .= $switcher; } else { @@ -466,49 +515,86 @@ sub secondary_menu { } sub create_submenu { - my ($link,$target,$title,$submenu) = @_; + my ($link,$target,$title,$submenu,$translate) = @_; return unless (ref($submenu) eq 'ARRAY'); my $disptarget; if ($target ne '') { $disptarget = ' target="'.$target.'"'; } - my $name; - if ($title eq 'Personal') { - if ($env{'user.name'} && $env{'user.domain'}) { - $name = &Apache::loncommon::plainname($env{'user.name'},$env{'user.domain'}); - } else { - $name = &mt($title); - } - } else { - $name = &mt($title); - } my $menu = '
  • '. ''. - ''.$name. + ''.$title. ''. ' ▼'. '
  • '; + + 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]; - 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; - } + 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;'; + $borderbot = 'border-bottom: 1px solid black;'; + } + + # href is a reference to another submenu + if (ref($href) eq 'ARRAY') { + $menu .= '
  • '; + $menu .= '

    ' + . $title . '

    '; + $menu .= ''; + $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"'; + } + + $menu .= '
  • '; + $menu .= '' . $title . ''; + $menu .= '
  • '; } - $menu .= '
  • '. - &mt($item->[1]).'
  • '; } } - $menu .= ''; return $menu; } @@ -554,8 +640,18 @@ sub innerregister { && $maptitle ne $coursetitle); push @crumbs, {text => $restitle, no_mt => 1} if $restitle; + my @tools; + if ($env{'request.filename'} =~ /\.page$/) { + my %breadcrumb_tools = &Apache::lonhtmlcommon::current_breadcrumb_tools(); + if (ref($breadcrumb_tools{'tools'}) eq 'ARRAY') { + @tools = @{$breadcrumb_tools{'tools'}}; + } + } &Apache::lonhtmlcommon::clear_breadcrumbs(); &Apache::lonhtmlcommon::add_breadcrumb(@crumbs); + if (@tools) { + &Apache::lonhtmlcommon::add_breadcrumb_tool('tools',@tools); + } } else { $resurl = $env{'request.noversionuri'}; my $courseurl = &Apache::lonnet::courseid_to_courseurl($env{'request.course.id'}); @@ -691,6 +787,9 @@ sub innerregister { my $currdir = '/priv/'.$udom.'/'.$uname.'/'.$thisdisfn; if ($currdir =~ m-/$-) { $is_const_dir = 1; + if ($thisdisfn eq '') { + $is_const_dir = 2; + } } else { $currdir =~ s|[^/]+$||; my $cleandisfn = &Apache::loncommon::escape_single($thisdisfn); @@ -733,7 +832,7 @@ ENDMENUITEMS # wishlist is only available for users with access to resource-pool # and links can only be set for resources within the resource-pool $menuitems .= (< 0){ } $menuitems.="&Notes&&annotate()&"; $menuitems.="Make notes and annotations about this resource&&1\n"; +my $is_mobile; +if ($env{'browser.mobile'}) { + $is_mobile = 1; +} - unless ($env{'request.noversionuri'}=~/\/(bulletinboard|smppg|navmaps|syllabus|aboutme|viewclasslist|portfolio)(\?|$)/) { + 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/})) { $menuitems.=(< 'WARNING!', + button => 'Done', + preamble => 'You are trying to end this timed event early.', + map => 'Confirming that you are done will cause the time to expire and prevent you from changing any answers in the current folder.', + resource => 'Confirming that you are done will cause the time to expire for this question, and prevent you from changing your answer(s).', + okdone => 'Click "OK" if you are completely finished.', + cancel => 'Click "Cancel" to continue working.', + proctor => 'Ask a proctor to enter the key, then click "OK" if you are completely finished.', + ok => 'OK', + exit => 'Cancel', + key => 'Key:', + nokey => 'A proctor key is required', + ); + my $confirm; + if (($type eq 'map') || ($type eq 'resource')) { + if ($proctor) { + $confirm = $lt{'preamble'}.' '.$lt{$type}; + if ($height !~ /^\d+$/) { + $height = 400; + } + if ($width !~ /^\d+$/) { + $width = 400; + } + return < + + + + + +
    +

    $lt{'preamble'} $lt{$type}

    +

    $lt{'proctor'}

    +
    + + +
    +

    $lt{'cancel'}

    +
    + + + +END + } else { + if ($height !~ /^\d+$/) { + $height = 320; + } + if ($width !~ /^\d+$/) { + $width = 320; + } + return < + + + + +
    +

    $lt{'preamble'} $lt{$type} $lt{'okdone'} $lt{'cancel'}

    +
    + + + +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'} ) { @@ -1469,8 +1759,26 @@ sub utilityfunctions { my $countdown = &countdown_toggle_js(); -return (<'."\n". + '
    '."\n". ' '."\n". ' '."\n". @@ -1798,7 +2115,7 @@ sub roles_selector { } } if (@submenu > 0) { - $switcher = &create_submenu('','',$switchtext,\@submenu); + $switcher = &create_submenu('','',&mt('Switch role'),\@submenu); } } return ($js,$form,$switcher); @@ -2069,10 +2386,12 @@ sub countdown_timer { } my $duedate = &Apache::lonnet::EXT("resource.0.duedate"); my @interval=&Apache::lonnet::EXT("resource.0.interval"); + my ($timelimit,$usesdone,$proctor,$secret); if (@interval > 1) { + ($timelimit,$usesdone,$proctor,$secret) = split(/_/,$interval[0]); my $first_access=&Apache::lonnet::get_first_access($interval[1]); if ($first_access > 0) { - if ($first_access+$interval[0] > time) { + if ($first_access+$timelimit > time) { $hastimeleft = 1; } } @@ -2080,11 +2399,16 @@ sub countdown_timer { if (($duedate && $duedate > time) || (!$duedate && $hastimeleft) || ($slot_name ne '' && $slothastime)) { - my ($collapse,$expand,$alttxt,$title,$currdisp); + my ($collapse,$expand,$alttxt,$title,$currdisp,$donebutton); if ((@interval > 1 && $hastimeleft) || ($type eq 'Task' && $slothastime)) { $currdisp = 'inline'; $collapse = '► '; + if ((@interval > 1) && ($hastimeleft)) { + if ($usesdone eq 'done') { + $donebutton = &done_button_js($interval[1],'','',$proctor); + } + } } else { $currdisp = 'none'; $expand = '◄ '; @@ -2094,8 +2418,9 @@ sub countdown_timer { $title = $alttxt.' '; } my $desc = &mt('Countdown to due date/time'); - return < $collapse