--- loncom/interface/lonmenu.pm 2019/07/27 16:25:41 1.369.2.75 +++ loncom/interface/lonmenu.pm 2021/01/04 17:27:54 1.369.2.79.2.4 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Routines to control the menu # -# $Id: lonmenu.pm,v 1.369.2.75 2019/07/27 16:25:41 raeburn Exp $ +# $Id: lonmenu.pm,v 1.369.2.79.2.4 2021/01/04 17:27:54 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -399,7 +399,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'}; @@ -422,6 +423,16 @@ sub secondary_menu { $showresv = 1; } } + 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) { + $grouptools ++; + } + } + } } my ($canmodifycoauthor); @@ -433,11 +444,6 @@ sub secondary_menu { } } - 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) { @@ -469,7 +475,7 @@ sub secondary_menu { next if $$menuitem[4] eq 'params' && (!$canmodpara && !$canviewpara); next if $$menuitem[4] eq 'nvcg' - && ($canviewgrps || !%groups); + && ($canviewgrps || !$grouptools); next if $$menuitem[4] eq 'showsyllabus' && !$showsyllabus; next if $$menuitem[4] eq 'showfeeds' @@ -729,7 +735,7 @@ sub innerregister { $newmail= 'swmenu.setstatus("you have","messages");'; } - my ($mapurl,$resurl,$crstype); + my ($mapurl,$resurl,$crstype,$navmap); if ($env{'request.course.id'}) { # @@ -737,13 +743,25 @@ sub innerregister { # $crstype = &Apache::loncommon::course_type(); 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 $restitle = &Apache::lonnet::gettitle($symb); - my @crumbs; + my (@crumbs,@mapcrumbs); + if (($env{'request.noversionuri'} ne '/adm/navmaps') && ($mapurl ne '') && + ($mapurl ne $env{'course.'.$env{'request.course.id'}.'.url'})) { + $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'})) { @@ -751,15 +769,22 @@ sub innerregister { 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); + } else { + push(@crumbs, {text => '...', + no_mt => 1}); + } } - push @crumbs, {text => $maptitle, no_mt => 1} if ($maptitle - && $maptitle ne 'default.sequence' - && $maptitle ne $coursetitle); - - push @crumbs, {text => $restitle, no_mt => 1} if $restitle; + unless ((@mapcrumbs) || (!$maptitle) || ($maptitle eq 'default.sequence') || + ($mapurl eq $env{'course.'.$env{'request.course.id'}.'.url'})) { + push @crumbs, {text => $maptitle, no_mt => 1, + href => &Apache::lonnet::clutter($mapurl).'?navmap=1'}; + } + if ($restitle && !@mapcrumbs) { + push(@crumbs,{text => $restitle, no_mt => 1}); + } my @tools; if ($env{'request.filename'} =~ /\.page$/) { my %breadcrumb_tools = &Apache::lonhtmlcommon::current_breadcrumb_tools(); @@ -993,13 +1018,14 @@ if ($env{'browser.mobile'}) { $is_mobile = 1; } - 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/})) { + 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/|default_\d+\.page$)})) { $menuitems.=(< 0) { @@ -2155,6 +2198,220 @@ function toggleCountdown() { END } +# This creates a "done button" for timed events. The confirmation box is a jQuery +# dialog widget. If the interval parameter requires a proctor key for the timed +# event to be marked done, there will also be a textbox where that can be entered. +# Clicking OK will set the value of LC_interval_done to 'true', and, if needed will +# set the value of LC_interval_done_proctorpass to the text entered in that box, +# and submit the corresponding form. +# +# The &zero_time() routine in lonhomework.pm is called when a page is rendered if +# LC_interval_done is true. +# +sub done_button_js { + my ($type,$width,$height,$proctor,$donebuttontext) = @_; + return unless (($type eq 'map') || ($type eq 'resource')); + my %lt = &Apache::lonlocal::texthash( + title => 'WARNING!', + 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 $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'}); + 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)) { + if ($res->is_problem()) { + push(@resources,$res); + } + } + } + foreach my $res (@resources) { + if (ref($res->parts()) eq 'ARRAY') { + foreach my $part (@{$res->parts()}) { + if (!$res->tries($part)) { + $missing++; + } else { + $tried++; + } + } + } + } + } + if ($missing) { + $lt{'miss'} .= '

'; + if ($type eq 'map') { + $lt{'miss'} .= &mt('Submissions are missing for [quant,_1,question part,question parts] in this folder.',$missing); + } else { + $lt{'miss'} .= &mt('Submissions are missing for [quant,_1,part] in this question.',$missing); + } + if ($missing > 1) { + $lt{'miss'} .= ' '.&mt('If you confirm you are done you will be unable to submit answers for them.').''; + } else { + $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; + if ($missing) { + $height += 60; + } + } + if ($width !~ /^\d+$/) { + $width = 400; + if ($missing) { + $width += 60; + } + } + return < + + + + + + +
+

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

+ $lt{'miss'} +

$lt{'proctor'}

+
+ + +
+

$lt{'cancel'}

+
+ + + +END + } else { + if ($height !~ /^\d+$/) { + $height = 320; + if ($missing) { + $height += 60; + } + } + if ($width !~ /^\d+$/) { + $width = 320; + if ($missing) { + $width += 60; + } + } + if ($missing) { + $lt{'miss'} = '

'.$lt{'miss'}.'

'; + } + return < + + + + + +

+

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

+
+ + + +END + } +} + sub utilityfunctions { my ($httphost) = @_; my $currenturl=&Apache::lonnet::clutter(&Apache::lonnet::fixversion((split(/\?/,$env{'request.noversionuri'}))[0])); @@ -2348,6 +2605,11 @@ 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 (\$) { \$(document).ready(function () { \$.single=function(a){return function(b){a[0]=b;return a}}(\$([1])); @@ -2941,10 +3203,20 @@ sub countdown_timer { } my $duedate = &Apache::lonnet::EXT("resource.0.duedate"); my @interval=&Apache::lonnet::EXT("resource.0.interval"); + my ($timelimit,$usesdone,$donebuttontext,$proctor,$secret); if (@interval > 1) { + ($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+$interval[0] > time) { + if ($first_access+$timelimit > time) { $hastimeleft = 1; } } @@ -2952,11 +3224,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,$donebuttontext); + } + } } else { $currdisp = 'none'; $expand = '◄ '; @@ -2967,7 +3244,7 @@ sub countdown_timer { } my $desc = &mt('Countdown to due date/time'); return < $collapse