--- loncom/interface/lonmenu.pm 2013/11/26 00:25:32 1.423 +++ loncom/interface/lonmenu.pm 2016/06/19 04:27:50 1.451 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Routines to control the menu # -# $Id: lonmenu.pm,v 1.423 2013/11/26 00:25:32 raeburn Exp $ +# $Id: lonmenu.pm,v 1.451 2016/06/19 04:27:50 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -124,9 +124,40 @@ dropdown list when mouse hovers over top (no hover psuedo class) via LC_hoverable class for <li> 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 <li> 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 + 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() @@ -211,6 +242,7 @@ 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); # each element of @primary contains following array: # (link url, icon path, alt text, link text, condition, position) @@ -219,6 +251,15 @@ sub primary_menu { || (($env{'user.name'} eq '') && ($env{'user.domain'} eq ''))) { $public = 1; } + my $rolecount; + if (($crstype eq 'Placement') && (!$env{'request.role.adv'})) { + my $update=$env{'user.update.time'}; + if (!$update) { + $update = $env{'user.login.time'}; + } + my %roles_in_env; + $rolecount = &Apache::lonroles::roles_from_env(\%roles_in_env,$update); + } foreach my $menuitem (@primary_menu) { # evaluate conditions next if ref($menuitem) ne 'ARRAY'; # @@ -236,8 +277,14 @@ sub primary_menu { && &Apache::loncommon::show_course(); ##term 'Courses' or next if $$menuitem[4] eq 'courses' ##'Roles' wanted && !&Apache::loncommon::show_course(); ## - my $title = $menuitem->[3]; + if (($crstype eq 'Placement') && (!$env{'request.role.adv'})) { + if ($menuitem->[4] eq 'courses') { + next unless ($rolecount>1); + } else { + next unless (($title eq 'Personal') || ($title eq 'Logout')); + } + } my $position = $menuitem->[5]; if ($position eq '') { $position = 'right'; @@ -253,6 +300,7 @@ sub primary_menu { my @primsub; if (ref($primary_submenu{$title}) eq 'ARRAY') { foreach my $item (@{$primary_submenu{$title}}) { + next if (($crstype eq 'Placement') && (!$env{'request.role.adv'})); next if (($item->[2] eq 'wishlist') && (!$env{'user.adv'})); next if ((($item->[2] eq 'portfolio') || ($item->[2] eq 'blog')) && @@ -260,13 +308,19 @@ sub primary_menu { undef,'tools'))); push(@primsub,$item); } + if ($title eq 'Personal' && $env{'user.name'} && $env{'user.domain'} ) { + $title = &Apache::loncommon::plainname($env{'user.name'},$env{'user.domain'}); + } else { + $title = &mt($title); + } if (@primsub > 0) { $menu{$position} .= &create_submenu($link,$target,$title,\@primsub,1); } elsif ($link) { - $menu{$position} .= '<li><a href="'.$link.'" target="'.$target.'">'.&mt($title).'</a></li>'; + $menu{$position} .= '<li><a href="'.$link.'" target="'.$target.'">'.$title.'</a></li>'; } } } elsif ($$menuitem[3] eq 'Help') { # special treatment for helplink + next if ($crstype eq 'Placement'); if ($public) { my $origmail = $Apache::lonnet::perlvar{'lonSupportEMail'}; my $defdom = &Apache::lonnet::default_login_domain(); @@ -339,7 +393,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'}; @@ -365,6 +420,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); @@ -375,17 +441,12 @@ 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) { # evaluate conditions next if ref($menuitem) ne 'ARRAY'; + next if (($crstype eq 'Placement') && ($$menuitem[3] ne 'Roles') && (!$env{'request.role.adv'})); next if $$menuitem[4] ne 'always' && ($$menuitem[4] ne 'author' && $$menuitem[4] ne 'cca') && !$env{'request.course.id'}; @@ -409,7 +470,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' @@ -477,54 +538,86 @@ 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 $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 = '<li class="LC_hoverable">'. + my $menu = '<li class="LC_hoverable '.$addclass.'">'. '<a href="'.$link.'"'.$disptarget.'>'. - '<span class="LC_nobreak">'.$name. + '<span class="LC_nobreak">'.$title. '<span class="LC_fontsize_small" style="font-weight:normal;">'. ' ▼</span></span></a>'. '<ul>'; + + # $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 .= '</ul></li>'; + + 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) = @_; + unless (@{$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; - if ($count == $numsub) { - $borderbot = 'border-bottom:1px solid black;'; - } - $menu .= '<li style="margin:0;padding:0;'. - $borderbot.'"><a href="'.$href.'">'; + my $title; + if ($translate) { - $menu .= &mt($item->[1]); + $title = &mt($item->[1]); } else { - $menu .= $item->[1]; + $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 .= '<li style="margin:0;padding:0;'.$bordertop . $borderbot . '">'; + $menu .= '<p><span class="LC_primary_menu_innertitle">' + . $title . '</span><span class="LC_primary_menu_innerarrow">▶</span></p>'; + $menu .= '<ul>'; + $menu .= &build_submenu($target, $href, $translate); + $menu .= '</ul>'; + $menu .= '</li>'; + } 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 .= '<li style="margin:0;padding:0;'. $bordertop . $borderbot .'">'; + $menu .= '<a href="'.$href.'"'.$target.'>' . $title . '</a>'; + $menu .= '</li>'; } - $menu .= '</a></li>'; } } - $menu .= '</ul></li>'; return $menu; } @@ -539,25 +632,25 @@ sub innerregister { undef(@inlineremote); - my ($mapurl,$resurl); + my ($mapurl,$resurl,$crstype); if ($env{'request.course.id'}) { +# +#course_type: Course, Community, or Placement +# + $crstype = &Apache::loncommon::course_type(); if ($env{'request.symb'}) { ($mapurl, my $rid, $resurl) = &Apache::lonnet::decode_symb(&Apache::lonnet::symbread()); my $coursetitle = $env{'course.'.$env{'request.course.id'}.'.description'}; my $maptitle = &Apache::lonnet::gettitle($mapurl); my $restitle = &Apache::lonnet::gettitle(&Apache::lonnet::symbread()); - -#SD -#course_type only Course and Community? -# my @crumbs; unless (($forcereg) && ($env{'request.noversionuri'} eq '/adm/navmaps') && - ($mapurl eq $env{'course.'.$env{'request.course.id'}.'.url'})) { - @crumbs = ({text => Apache::loncommon::course_type() - . ' Contents', + ($mapurl eq $env{'course.'.$env{'request.course.id'}.'.url'}) || + (($crstype eq 'Placement') && (!$env{'request.role.adv'}))) { + @crumbs = ({text => $crstype.' Contents', href => "Javascript:gopost('/adm/navmaps','')"}); } if ($mapurl ne $env{'course.'.$env{'request.course.id'}.'.url'}) { @@ -565,17 +658,28 @@ sub innerregister { no_mt => 1}); } - push @crumbs, {text => $maptitle, no_mt => 1} if ($maptitle - && $maptitle ne 'default.sequence' - && $maptitle ne $coursetitle); + unless (($crstype eq 'Placement') || (!$env{'request.role.adv'})) { + 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; + 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'}); - my $crstype = &Apache::loncommon::course_type(); my $title = &mt('View Resource'); if ($resurl =~ m{^\Q/uploaded$courseurl/supplemental/\E(default|\d+)/}) { &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['folderpath','title']); @@ -707,6 +811,13 @@ sub innerregister { my $currdir = '/priv/'.$udom.'/'.$uname.'/'.$thisdisfn; if ($currdir =~ m-/$-) { $is_const_dir = 1; + if ($thisdisfn eq '') { + 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|[^/]+$||; my $cleandisfn = &Apache::loncommon::escape_single($thisdisfn); @@ -734,10 +845,30 @@ ENDMENUITEMS # We are in a course and looking at a registered URL # Should probably be in mydesk.tab # - $menuitems=(<<ENDMENUITEMS); -c&3&1 + $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&3&forw.png&&&gopost('/adm/flip','forward:'+currentURL)&Next content resource&&3 +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 .= (<<ENDMENUITEMS); + c&6&3 c&8&1 c&8&2 @@ -765,11 +896,15 @@ if(length($annotation) > 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.=(<<ENDREALRES); -s&6&3&catalog.png&Info&info[_1]&catalog_info()&Show Metadata +s&6&3&catalog.png&Info&info[_1]&catalog_info('$is_mobile')&Show Metadata ENDREALRES } unless (($env{'request.noversionuri'} =~ m{^/uploaded/$match_domain/$match_courseid/docs/}) || @@ -820,21 +955,36 @@ ENDMENUITEMS } } } + my $showprogress; + if (($crstype eq 'Placement') && (!$env{'request.role.adv'})) { + $showprogress = &placement_progress(); + } - my $addremote=0; - foreach (@inlineremote) { if ($_ ne '') { $addremote=1; last;} } - if ($addremote) { + my $addremote=0; + foreach (@inlineremote) { if ($_ ne '') { $addremote=1; last;} } + if ($addremote) { + my $countdown; + if ($env{'request.filename'} =~ /\.page$/) { + my %breadcrumb_tools = &Apache::lonhtmlcommon::current_breadcrumb_tools(); + if (ref($breadcrumb_tools{'tools'}) eq 'ARRAY') { + $countdown = $breadcrumb_tools{'tools'}[0]; + } + } else { + $countdown = &countdown_timer(); + } &Apache::lonhtmlcommon::clear_breadcrumb_tools(); &Apache::lonhtmlcommon::add_breadcrumb_tool( 'navigation', @inlineremote[21,23]); - my $countdown = &countdown_timer(); if (&hidden_button_check() 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) { @@ -853,10 +1003,23 @@ 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) { + if ((($ENV{'SERVER_PORT'} == 443) || + ($Apache::lonnet::protocol{$Apache::lonnet::perlvar{'lonHostID'}} eq 'https')) && + (&Apache::lonnet::usertools_access($env{'user.name'},$env{'user.domain'},'webdav'))) { + $topic_help = 'Authoring_WebDAV,Authoring_WebDAV_Mac_10v6,Authoring_WebDAV_Mac_10v10,'. + 'Authoring_WebDAV_Windows_v7,Authoring_WebDAV_Linux_Centos'; + $topic_help_text = 'About WebDAV access'; + } } - return &Apache::lonhtmlcommon::scripttag('', 'start') - . &Apache::lonhtmlcommon::breadcrumbs(undef,undef,0) + . &Apache::lonhtmlcommon::breadcrumbs(undef,undef,0,'','','','',$topic_help,$topic_help_text) . &Apache::lonhtmlcommon::scripttag('', 'end'); } @@ -1028,6 +1191,7 @@ sub prepare_functions { if (($env{'form.folderpath'} =~ /^supplemental/) && (&Apache::lonnet::allowed('mdc',$env{'request.course.id'})) && (($resurl =~ m{^/adm/wrapper/ext/}) || + ($resurl =~ m{^/adm/$cdom/$cnum/\d+/exttools?$}) || ($resurl =~ m{^/uploaded/$cdom/$cnum/supplemental/}) || ($resurl eq '/adm/supplemental') || ($resurl =~ m{^/public/$cdom/$cnum/syllabus$}) || @@ -1226,7 +1390,10 @@ sub rawconfig { my $pub=($env{'request.state'} eq 'published'); my $con=($env{'request.state'} eq 'construct'); my $rol=$env{'request.role'}; - my $requested_domain = $env{'request.role.domain'}; + my $requested_domain; + if ($rol) { + $requested_domain = $env{'request.role.domain'}; + } foreach my $line (@desklines) { my ($row,$col,$pro,$prt,$img,$top,$bot,$act,$desc,$cat)=split(/\:/,$line); $prt=~s/\$uname/$uname/g; @@ -1240,7 +1407,13 @@ sub rawconfig { next if ($crstype ne 'Community'); $prt=~s/\$cmty/$crs/g; } - $prt=~s/\$requested_domain/$requested_domain/g; + if ($prt =~ m/\$requested_domain/) { + if ((!$requested_domain) && ($pro eq 'pbre') && ($env{'user.adv'})) { + $prt=~s/\$requested_domain/$env{'user.domain'}/g; + } else { + $prt=~s/\$requested_domain/$requested_domain/g; + } + } if ($category_names{$cat}!~/\w/) { $cat='oth'; } if ($pro eq 'clear') { $output.=&clear($row,$col); @@ -1276,8 +1449,9 @@ sub rawconfig { next; } } - if (&Apache::lonnet::allowed($priv,$prt)) { - $output.=&switch($uname,$udom,$row,$col,$img,$top,$bot,$act,$desc,$cat); + if ((($priv eq 'bre') && (&Apache::lonnet::allowed($priv,$prt) eq 'F')) || + (($priv ne 'bre') && (&Apache::lonnet::allowed($priv,$prt)))) { + $output.=&switch($uname,$udom,$row,$col,$img,$top,$bot,$act,$desc,$cat); } } elsif ($pro eq 'course') { if (($env{'request.course.fn'}) && ($crstype ne 'Community')) { @@ -1387,7 +1561,7 @@ sub rawconfig { sub check_for_rcrs { my $showreqcrs = 0; - my @reqtypes = ('official','unofficial','community'); + my @reqtypes = ('official','unofficial','community','textbook','placement'); foreach my $type (@reqtypes) { if (&Apache::lonnet::usertools_access($env{'user.name'}, $env{'user.domain'}, @@ -1453,6 +1627,213 @@ 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 $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() }); + } 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'} .= '<p class="LC_error">'; + 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.').'</span>'; + } else { + $lt{'miss'} .= ' '.&mt('If you confirm you are done you will be unable to submit an answer for it.').'</p>'; + } + } + $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 <<END; +<form method="post" name="LCdoneButton" action=""> + <input type="hidden" name="LC_interval_done" value="" /> + <input type="hidden" name="LC_interval_done_proctorpass" value="" /> + <button id="LC_done-confirm-opener" type="button">$donebuttontext</button> +</form> + +<div id="LC_done-confirm" title="$lt{'title'}"> + <p>$lt{'preamble'} $lt{$type}</p> + $lt{'miss'} + <p>$lt{'proctor'}</p> + <form name="LCdoneButtonProctor" action=""> + <label>$lt{'key'}<input type="password" name="LC_interval_done_proctorkey" value="" /></label> + <input type="submit" tabindex="-1" style="position:absolute; top:-1000px" /> + </form> + <p>$lt{'cancel'}</p> +</div> + +<script type="text/javascript"> +// <![CDATA[ + \$( "#LC_done-confirm" ).dialog({ autoOpen: false }); + \$( "#LC_done-confirm-opener" ).on("click", function() { + \$( "#LC_done-confirm" ).dialog("open"); + \$( "#LC_done-confirm" ).dialog({ + height: $height, + width: $width, + modal: true, + resizable: false, + buttons: [ + { + text: "$lt{'ok'}", + click: function() { + var proctorkey = \$( '[name="LC_interval_done_proctorkey"]' )[0].value; + if ((proctorkey == '') || (proctorkey == null)) { + alert("$lt{'nokey'}"); + } else { + \$( '[name="LC_interval_done"]' )[0].value = 'true'; + \$( '[name="LC_interval_done_proctorpass"]' )[0].value = proctorkey; + \$( '[name="LCdoneButton"]' )[0].submit(); + } + }, + }, + { + text: "$lt{'exit'}", + click: function() { + \$("#LC_done-confirm").dialog( "close" ); + } + } + ], + close: function() { + \$( '[name="LC_interval_done_proctorkey"]' )[0].value = ''; + } + }); + \$( "#LC_done-confirm" ).find( "form" ).on( "submit", function( event ) { + event.preventDefault(); + \$( '[name="LC_interval_done"]' )[0].value = 'true'; + \$( '[name="LC_interval_done_proctorpass"]' )[0].value = \$( '[name="LC_interval_done_proctorkey"]' )[0].value; + \$( '[name="LCdoneButton"]' )[0].submit(); + }); +}); + +// ]]> +</script> + +END + } else { + if ($height !~ /^\d+$/) { + $height = 320; + if ($missing) { + $height += 60; + } + } + if ($width !~ /^\d+$/) { + $width = 320; + if ($missing) { + $width += 60; + } + } + if ($missing) { + $lt{'miss'} = '</p>'.$lt{'miss'}.'<p>'; + } + return <<END; + +<form method="post" name="LCdoneButton" action=""> + <input type="hidden" name="LC_interval_done" value="" /> + <button id="LC_done-confirm-opener" type="button">$donebuttontext</button> +</form> + +<div id="LC_done-confirm" title="$lt{'title'}"> + <p>$lt{'preamble'} $lt{$type} $lt{'miss'} $lt{'okdone'} $lt{'cancel'}</p> +</div> + +<script type="text/javascript"> +// <![CDATA[ +\$( "#LC_done-confirm" ).dialog({ autoOpen: false }); +\$( "#LC_done-confirm-opener" ).click(function() { + \$( "#LC_done-confirm" ).dialog( "open" ); + \$( "#LC_done-confirm" ).dialog({ + resizable: false, + height: $height, + width: $width, + modal: true, + buttons: [ + { + text: "$lt{'ok'}", + click: function() { + \$( this ).dialog( "close" ); + \$( '[name="LC_interval_done"]' )[0].value = 'true'; + \$( '[name="LCdoneButton"]' )[0].submit(); + }, + }, + { + text: "$lt{'exit'}", + click: function() { + \$( this ).dialog( "close" ); + }, + }, + ], + }); +}); +// ]]> +</script> + +END + } +} + sub utilityfunctions { my ($httphost) = @_; my $currenturl=&Apache::lonnet::clutter(&Apache::lonnet::fixversion((split(/\?/,$env{'request.noversionuri'}))[0])); @@ -1489,8 +1870,26 @@ sub utilityfunctions { my $countdown = &countdown_toggle_js(); + 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 (<<ENDUTILITY) - var host="$httphost"; + $hostvar var currentURL=unescape("$esc_url"); var reloadURL=unescape("$esc_url"); var currentSymb=unescape("$esc_symb"); @@ -1501,7 +1900,8 @@ $jumptores function gopost(url,postdata) { if (url!='') { - this.document.server.action=host+url; + var lcHostname = setLCHost(); + this.document.server.action=lcHostname+url; this.document.server.postdata.value=postdata; this.document.server.command.value=''; this.document.server.url.value=''; @@ -1512,7 +1912,8 @@ function gopost(url,postdata) { function gocmd(url,cmd) { if (url!='') { - this.document.server.action=host+url; + var lcHostname = setLCHost(); + this.document.server.action=lcHostname+url; this.document.server.postdata.value=''; this.document.server.command.value=cmd; this.document.server.url.value=currentURL; @@ -1564,22 +1965,29 @@ function golist(url) { if (url!='' && url!= null) { currentURL = null; currentSymb= null; - top.location.href=host+url; + var lcHostname = setLCHost(); + top.location.href=lcHostname+url; } } -function catalog_info() { - openMyModal(window.location.pathname+'.meta',500,400,'yes'); +function catalog_info(isMobile) { + if (isMobile == 1) { + openMyModal(window.location.pathname+'.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'); + } } function chat_win() { - lonchat=window.open(host+'/res/adm/pages/chatroom.html',"LONchat",'height=320,width=480,resizable=yes,location=no,menubar=no,toolbar=no'); + var lcHostname = setLCHost(); + lonchat=window.open(lcHostname+'/res/adm/pages/chatroom.html',"LONchat",'height=320,width=480,resizable=yes,location=no,menubar=no,toolbar=no'); } function group_chat(group) { - var url = host+'/adm/groupchat?group='+group; + var lcHostname = setLCHost(); + var url = lcHostname+'/adm/groupchat?group='+group; var winName = 'LONchat_'+group; grpchat=window.open(url,winName,'height=320,width=280,resizable=yes,location=no,menubar=no,toolbar=no'); } @@ -1599,12 +2007,13 @@ function annotate() { function open_StoredLinks_Import(rat) { var newWin; + var lcHostname = setLCHost(); if (rat) { - newWin = window.open(host+'/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(host+'/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(); @@ -1674,7 +2083,7 @@ sub roles_selector { my $now = time; my (%courseroles,%seccount,%courseprivs); my $is_cc; - my ($js,$form,$switcher,$switchtext); + my ($js,$form,$switcher); my $ccrole; if ($crstype eq 'Community') { $ccrole = 'co'; @@ -1762,7 +2171,6 @@ sub roles_selector { } } } - $switchtext = 'Switch role'; # do not translate here my @roles_order = ($ccrole,'in','ta','ep','ad','st'); my $numdiffsec; if (keys(%seccount) == 1) { @@ -1818,7 +2226,7 @@ sub roles_selector { } } if (@submenu > 0) { - $switcher = &create_submenu('','',$switchtext,\@submenu); + $switcher = &create_submenu('','',&mt('Switch role'),\@submenu); } } return ($js,$form,$switcher); @@ -2089,10 +2497,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; } } @@ -2100,11 +2518,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 = '◄ '; @@ -2114,8 +2537,9 @@ sub countdown_timer { $title = $alttxt.' '; } my $desc = &mt('Countdown to due date/time'); - return <<END; + return <<END; +$donebutton <a href="javascript:toggleCountdown();" class="LC_menubuttons_link"> <span id="ddcountcollapse" class="LC_menubuttons_inline_text"> $collapse @@ -2130,6 +2554,13 @@ END return; } +sub placement_progress { + my ($totalpoints,$incomplete) = &Apache::lonplacementtest::check_completion(undef,undef,1); + my $complete = 100 - $incomplete; + return '<span class="LC_placement_prog">'. + &mt('Test is [_1]% complete',$complete).'</span>'; +} + # ================================================================ Main Program BEGIN {