--- loncom/interface/lonmenu.pm 2016/04/02 04:30:20 1.443 +++ loncom/interface/lonmenu.pm 2016/11/09 14:23:36 1.461 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Routines to control the menu # -# $Id: lonmenu.pm,v 1.443 2016/04/02 04:30:20 raeburn Exp $ +# $Id: lonmenu.pm,v 1.461 2016/11/09 14:23:36 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -124,9 +124,13 @@ dropdown list when mouse hovers over top (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. +Inputs: 6 - (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, + (e) boolean to indicate whether to call &mt() to translate + name of menu item, + (f) optional class for
  • element in primary menu, for which + sub menu is being generated. 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 @@ -382,8 +386,10 @@ sub secondary_menu { undef($canviewroster); } my $canviewgrps = &Apache::lonnet::allowed('vcg', $crs_sec); - my $canmodifyuser = &Apache::lonnet::allowed('cst', $crs_sec); + my $canmodifyuser = &Apache::lonnet::allowed('cst', $crs_sec); + my $canviewusers = &Apache::lonnet::allowed('vcl', $crs_sec); my $canviewwnew = &Apache::lonnet::allowed('whn', $crs_sec); + my $canviewpara = &Apache::lonnet::allowed('vpa', $crs_sec); my $canmodpara = &Apache::lonnet::allowed('opa', $crs_sec); my $canvgr = &Apache::lonnet::allowed('vgr', $crs_sec); my $canmgr = &Apache::lonnet::allowed('mgr', $crs_sec); @@ -410,7 +416,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; @@ -452,18 +458,18 @@ sub secondary_menu { && $canvgr; next if $$menuitem[4] eq 'vgr' && !$canvgr; - next if $$menuitem[4] eq 'cst' - && !$canmodifyuser; - next if $$menuitem[4] eq 'ncst' - && ($canmodifyuser || !$canviewroster); + next if $$menuitem[4] eq 'viewusers' + && !$canmodifyuser && !$canviewusers; + next if $$menuitem[4] eq 'noviewusers' + && ($canmodifyuser || $canviewusers || !$canviewroster); next if $$menuitem[4] eq 'mgr' && !$canmgr; next if $$menuitem[4] eq 'showresv' && !$showresv; next if $$menuitem[4] eq 'whn' && !$canviewwnew; - next if $$menuitem[4] eq 'opa' - && !$canmodpara; + next if $$menuitem[4] eq 'params' + && (!$canmodpara && !$canviewpara); next if $$menuitem[4] =~ /showgroups$/ && !$canviewgrps && !$grouptools; @@ -534,13 +540,13 @@ sub secondary_menu { } sub create_submenu { - my ($link,$target,$title,$submenu,$translate) = @_; + my ($link,$target,$title,$submenu,$translate,$addclass) = @_; return unless (ref($submenu) eq 'ARRAY'); my $disptarget; if ($target ne '') { $disptarget = ' target="'.$target.'"'; } - my $menu = '
  • '. + my $menu = '
  • '. ''. ''.$title. ''. @@ -560,7 +566,7 @@ sub create_submenu { # see perldoc create_submenu documentation for further information sub build_submenu { my ($target, $submenu, $translate, $first_level) = @_; - if (!defined(@{$submenu})) { + unless (@{$submenu}) { return ''; } @@ -618,7 +624,7 @@ sub build_submenu { } sub innerregister { - my ($forcereg,$bread_crumbs,$group) = @_; + my ($forcereg,$bread_crumbs,$group,$pagebuttonshide) = @_; my $const_space = ($env{'request.state'} eq 'construct'); my $is_const_dir = 0; @@ -745,7 +751,7 @@ sub innerregister { if ($env{'request.filename'}=~/$LONCAPA::assess_re/) { push(@privs,('mgr','vgr')); } - push(@privs,'opa'); + push(@privs,('opa','vpa')); } foreach my $priv (@privs) { $perms{$priv} = &Apache::lonnet::allowed($priv,$env{'request.course.id'}); @@ -769,7 +775,7 @@ sub innerregister { 'Content Submissions'); } } - if (($env{'request.symb'} ne '') && ($perms{'opa'})) { + if (($env{'request.symb'} ne '') && (($perms{'opa'}) || ($perms{'vpa'}))) { &switch('','',7,3,'pparm.png','Content Settings','parms[_2]', "gocmd('/adm/parmset','set')", 'Content Settings'); @@ -808,7 +814,11 @@ sub innerregister { if ($currdir =~ m-/$-) { $is_const_dir = 1; if ($thisdisfn eq '') { - $is_const_dir = 2; + unless (($env{'request.course.id'}) && + ($env{'course.'.$env{'request.course.id'}.'.num'} eq $uname) && + ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $udom)) { + $is_const_dir = 2; + } } } else { $currdir =~ s|[^/]+$||; @@ -840,11 +850,27 @@ ENDMENUITEMS $menuitems = "c&3&1"; if (($crstype ne 'Placement') || ($env{'request.role.adv'})) { $menuitems.=" -s&2&1&back.png&&&gopost('/adm/flip','back:'+currentURL)&Previous content resource&&1"; +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"; + } else { +# Suppress display of backward arrow for Placement Tests +# Suppress display of forward arrow for Placement Tests if this is the last resource. + my $showforw = 1; + if ($env{'request.symb'}) { + my $navmap = Apache::lonnavmaps::navmap->new(); + if (ref($navmap)) { + if (&Apache::lonplacementtest::is_lastres($env{'request.symb'},$navmap)) { + $showforw = 0; + } + } + } + if ($showforw) { + $menuitems.=" +s&2&3&forw.png&&&gopost('/adm/flip','forward:'+currentURL)&Next content resource&&3"; + } } $menuitems .= (<[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); } + if ($showprogress) { + &Apache::lonhtmlcommon::add_breadcrumb_tool('tools',$showprogress); + } } else { my @tools = @inlineremote[93,91,81,82,83]; if ($countdown) { @@ -971,6 +1010,10 @@ ENDMENUITEMS } &advtools_crumbs(@inlineremote); } + } else { + if ($showprogress) { + &Apache::lonhtmlcommon::add_breadcrumb_tool('tools',$showprogress); + } } my ($topic_help,$topic_help_text); if ($is_const_dir == 2) { @@ -1001,11 +1044,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.suppurl'}),$env{'form.todocs'}, + $suppanchor); } if ($jscall) { my $icon = 'pcstr.png'; @@ -1162,9 +1210,13 @@ sub prepare_functions { ($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'}),'<>&"')); &switch('','',7,4,'docs-22x22.png','Edit Folder','parms[_2]', - "location.href='/adm/coursedocs?command=direct&forcesupplement=1&supppath=$esc_path'", + "location.href='/adm/coursedocs?command=direct&forcesupplement=1&supppath=$esc_path&anchor=$suppanchor'", 'Folder/Page Content'); } } @@ -1602,11 +1654,10 @@ END # LC_interval_done is true. # sub done_button_js { - my ($type,$width,$height,$proctor) = @_; + my ($type,$width,$height,$proctor,$donebuttontext) = @_; return unless (($type eq 'map') || ($type eq 'resource')); my %lt = &Apache::lonlocal::texthash( title => '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).', @@ -1619,10 +1670,8 @@ sub done_button_js { nokey => 'A proctor key is required', ); my $navmap = Apache::lonnavmaps::navmap->new(); - my ($missing,$tried); + my ($missing,$tried) = (0,0); if (ref($navmap)) { - $missing=0; - $tried=0; my @resources=(); if ($type eq 'map') { my ($mapurl,$rid,$resurl)=&Apache::lonnet::decode_symb($env{'request.symb'}); @@ -1636,13 +1685,7 @@ sub done_button_js { } } foreach my $res (@resources) { - if ($res->singlepart()) { - if (!$res->tries()) { - $missing++; - } else { - $tried++; - } - } else { + if (ref($res->parts()) eq 'ARRAY') { foreach my $part (@{$res->parts()}) { if (!$res->tries($part)) { $missing++; @@ -1666,6 +1709,7 @@ sub done_button_js { $lt{'miss'} .= ' '.&mt('If you confirm you are done you will be unable to submit an answer for it.').'

    '; } } + $donebuttontext = &HTML::Entities::encode($donebuttontext,'<>&"'); if ($proctor) { if ($height !~ /^\d+$/) { $height = 400; @@ -1683,14 +1727,14 @@ sub done_button_js {
    - +

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

    $lt{'miss'}

    $lt{'proctor'}

    -
    +
    @@ -1764,7 +1808,7 @@ END
    - +
    @@ -2062,7 +2106,7 @@ sub roles_selector { } else { $ccrole = 'cc'; } - my ($priv,$gotsymb,$destsymb); + my ($privref,$gotsymb,$destsymb); my $destinationurl = $ENV{'REQUEST_URI'}; if ($destinationurl =~ /\?symb=/) { $gotsymb = 1; @@ -2083,12 +2127,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))) { @@ -2098,7 +2145,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,$privref); } else { my %gotnosection; foreach my $item (keys(%env)) { @@ -2114,7 +2163,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"; @@ -2152,7 +2201,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,$privref); $form = '
    '."\n". ' 1) { + if ($env{"user.role.dh./$cdom/"}) { + my ($start,$end)=split(/\./,$env{"user.role.dh./$cdom/"}); + my $now = time; + if (!($start && ($now<$start)) & !($end && ($now>$end))) { + 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); + $numsec += scalar(keys(%sections_count)); + $allseclist = join(',',sort(keys(%sections_count))); + &Apache::lonnet::do_cache_new('courseseclist',$cdom.'_'.$cnum,$allseclist); + } + my (%adhoc,$gotprivs); + my $prefix = "cr/$cdom/$cdom".'-domainconfig'; + foreach my $role (@customroles) { + next if (($role eq '') || ($role =~ /\W/)); + $seccount->{"$prefix/$role"} = $numsec; + $courseroles->{"$prefix/$role"} = \@sections; + 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 '') { + $adhoc{$rolename} = $roledefs{$key}; + } + } + &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}); + } + } + } + } + } + } + } + return; +} + sub jump_to_role { - my ($cdom,$cnum,$seccount,$courseroles,$courseprivs,$priv) = @_; + my ($cdom,$cnum,$seccount,$courseroles,$courseprivs,$privref) = @_; my %lt = &Apache::lonlocal::texthash( this => 'This role has section(s) associated with it.', ente => 'Enter a specific section.', @@ -2308,29 +2438,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"; } } @@ -2427,13 +2554,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') { @@ -2469,9 +2596,17 @@ sub countdown_timer { } my $duedate = &Apache::lonnet::EXT("resource.0.duedate"); my @interval=&Apache::lonnet::EXT("resource.0.interval"); - my ($timelimit,$usesdone,$proctor,$secret); + my ($timelimit,$usesdone,$donebuttontext,$proctor,$secret); if (@interval > 1) { - ($timelimit,$usesdone,$proctor,$secret) = split(/_/,$interval[0]); + ($timelimit,my $donesuffix) = split(/_/,$interval[0],2); + if ($donesuffix =~ /^done\:([^\:]+)\:(.*)$/) { + $usesdone = 'done'; + $donebuttontext = $1; + (undef,$proctor,$secret) = split(/_/,$2); + } elsif ($donesuffix =~ /^done(|_.+)$/) { + $donebuttontext = &mt('Done'); + ($usesdone,$proctor,$secret) = split(/_/,$donesuffix); + } my $first_access=&Apache::lonnet::get_first_access($interval[1]); if ($first_access > 0) { if ($first_access+$timelimit > time) { @@ -2489,7 +2624,7 @@ sub countdown_timer { $collapse = '► '; if ((@interval > 1) && ($hastimeleft)) { if ($usesdone eq 'done') { - $donebutton = &done_button_js($interval[1],'','',$proctor); + $donebutton = &done_button_js($interval[1],'','',$proctor,$donebuttontext); } } } else { @@ -2518,6 +2653,13 @@ END return; } +sub placement_progress { + my ($totalpoints,$incomplete) = &Apache::lonplacementtest::check_completion(undef,undef,1); + my $complete = 100 - $incomplete; + return ''. + &mt('Test is [_1]% complete',$complete).''; +} + # ================================================================ Main Program BEGIN {