--- loncom/interface/lonmenu.pm 2016/08/13 21:14:50 1.369.2.57
+++ loncom/interface/lonmenu.pm 2017/10/14 22:37:15 1.369.2.71.4.4
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Routines to control the menu
#
-# $Id: lonmenu.pm,v 1.369.2.57 2016/08/13 21:14:50 raeburn Exp $
+# $Id: lonmenu.pm,v 1.369.2.71.4.4 2017/10/14 22:37:15 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -132,6 +132,33 @@ Inputs: 6 - (a) link and (b) target for
(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
+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()
@@ -286,6 +313,11 @@ sub primary_menu {
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) = @_;
+ 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;';
- }
- unless (($href eq '') || ($href =~ /^\#/)) {
- $target = ' target="_top"';
- }
- $menu .= '
';
}
}
- $menu .= '';
return $menu;
}
@@ -613,7 +678,7 @@ sub registerurl {
}
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;
@@ -634,21 +699,31 @@ sub innerregister {
$newmail= 'swmenu.setstatus("you have","messages");';
}
- my ($mapurl,$resurl);
+ my ($mapurl,$resurl,$navmap);
if ($env{'request.course.id'}) {
+#
+#course_type: Course or Community
+#
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);
-#SD
-#course_type only Course and Community?
-#
- my @crumbs;
+ my (@crumbs,@mapcrumbs);
+ if (($env{'request.noversionuri'} ne '/adm/navmaps') && ($mapurl ne '')) {
+ $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'})) {
@@ -657,15 +732,20 @@ 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 => $mapurl};
+ }
+ 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();
@@ -759,12 +839,13 @@ sub innerregister {
$cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
$cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
$perms{'mdc'} = &Apache::lonnet::allowed('mdc',$env{'request.course.id'});
+ $perms{'cev'} = &Apache::lonnet::allowed('cev',$env{'request.course.id'});
my @privs;
if ($env{'request.symb'} ne '') {
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'});
@@ -790,7 +871,7 @@ sub innerregister {
'Content Submissions');
}
}
- if (($env{'request.symb'} ne '') && ($perms{'opa'})) {
+ if (($env{'request.symb'} ne '') && (($perms{'opa'}) || ($perms{'vpa'}))) {
$hwkadd .= &switch('','',7,3,'pparm.png','Content Settings',
'parms[_2]',"gocmd('/adm/parmset','set')",
'Content Settings');
@@ -800,7 +881,7 @@ sub innerregister {
#
# This applies to items inside a folder/page modifiable in the course.
#
- if (($env{'request.symb'}=~/^uploaded/) && ($perms{'mdc'})) {
+ if (($env{'request.symb'}=~/^uploaded/) && (($perms{'mdc'}) || ($perms{'cev'}))) {
my $text = 'Edit Folder';
if (($mapurl =~ /\.page$/) ||
($env{'request.symb'}=~
@@ -899,7 +980,7 @@ 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|ext\.tool)(\?|$)/) {
if ((!$env{'request.enc'}) && ($env{'request.noversionuri'} !~ m{^/adm/wrapper/ext/}) && ($env{'request.noversionuri'} !~ m{^/uploaded/$match_domain/$match_courseid/docs/})) {
$menuitems.=(<[0];
}
+ $buttonshide = $pagebuttonshide;
} else {
$countdown = &countdown_timer();
+ $buttonshide = &hidden_button_check();
}
- if (&hidden_button_check() eq 'yes') {
+
+ &Apache::lonhtmlcommon::clear_breadcrumb_tools();
+
+ &Apache::lonhtmlcommon::add_breadcrumb_tool(
+ 'navigation', @inlineremote[21,23]);
+
+ if ($buttonshide eq 'yes') {
if ($countdown) {
&Apache::lonhtmlcommon::add_breadcrumb_tool('tools',$countdown);
}
@@ -1335,9 +1418,9 @@ sub get_editbutton {
my ($cfile,$home,$switchserver,$forceedit,$forceview,$forcereg) = @_;
my $jscall;
if (($forceview) && ($env{'form.todocs'})) {
- my ($folderpath,$command);
+ my ($folderpath,$command,$navmap);
if ($env{'request.symb'}) {
- $folderpath = &Apache::loncommon::symb_to_docspath($env{'request.symb'});
+ $folderpath = &Apache::loncommon::symb_to_docspath($env{'request.symb'},\$navmap);
} elsif ($env{'form.folderpath'} =~ /^supplemental/) {
$folderpath = $env{'form.folderpath'};
$command = '&forcesupplement=1';
@@ -1345,11 +1428,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';
@@ -1396,8 +1484,9 @@ sub prepare_functions {
}
my $editbutton = '';
+ my $viewsrcbutton = '';
#
-# Determine whether or not to display 'Edit' icon/button
+# Determine whether or not to display 'Edit' or 'View Source' icon/button
#
if ($resurl =~ m{^/?adm/($match_domain)/($match_username)/aboutme$}) {
my $file=&Apache::lonnet::declutter($env{'request.filename'});
@@ -1428,17 +1517,30 @@ sub prepare_functions {
# This applies in course context
#
if (($perms{'mdc'}) &&
- (($resurl eq "/public/$cdom/$cnum/syllabus") ||
- ($resurl =~ m{^/uploaded/$cdom/$cnum/portfolio/syllabus/}))) {
- $cfile = $resurl;
+ (($resurl =~ m{^/?public/$cdom/$cnum/syllabus}) ||
+ ($resurl =~ m{^/?uploaded/$cdom/$cnum/portfolio/syllabus/}) ||
+ (($resurl =~ m{^/?uploaded/$cdom/$cnum/default_\d+\.sequence$}) && ($env{'form.navmap'})))) {
+ if ($resurl =~ m{^/}) {
+ $cfile = $resurl;
+ } else {
+ $cfile = "/$resurl";
+ }
$home = &Apache::lonnet::homeserver($cnum,$cdom);
if ($env{'form.forceedit'}) {
$forceview = 1;
} else {
$forceedit = 1;
}
- $editbutton = &get_editbutton($cfile,$home,$switchserver,
- $forceedit,$forceview,$forcereg);
+ if ($cfile =~ m{^/uploaded/$cdom/$cnum/default_\d+\.sequence$}) {
+ my $text = 'Edit Folder';
+ &switch('','',7,4,'docs-22x22.png','Edit Folder','parms[_2]',
+ "gocmd('/adm/coursedocs','direct')",
+ 'Folder/Page Content');
+ $editbutton = 1;
+ } else {
+ $editbutton = &get_editbutton($cfile,$home,$switchserver,
+ $forceedit,$forceview,$forcereg);
+ }
} elsif (($resurl eq '/adm/extresedit') &&
(($env{'form.symb'}) || ($env{'form.folderpath'}))) {
($cfile,$home,$switchserver,$forceedit,$forceview) =
@@ -1467,6 +1569,29 @@ sub prepare_functions {
$editbutton = &get_editbutton($cfile,$home,$switchserver,
$forceedit,$forceview,$forcereg);
}
+ if ((($cfile eq '') || (!$editbutton)) &&
+ ($resurl =~ /$LONCAPA::assess_re/)) {
+ my $showurl = &Apache::lonnet::clutter($resurl);
+ if ((&Apache::lonnet::allowed('cre','/')) &&
+ (&Apache::lonnet::metadata($resurl,'sourceavail') eq 'open')) {
+ $viewsrcbutton = 1;
+ } elsif (&Apache::lonnet::allowed('vxc',$env{'request.course.id'})) {
+ if ($showurl =~ m{^\Q/res/$cdom/\E($match_username)/}) {
+ my $auname = $1;
+ if (($env{'request.course.adhocsrcaccess'} ne '') &&
+ (grep(/^\Q$auname\E$/,split(/,/,$env{'request.course.adhocsrcaccess'})))) {
+ $viewsrcbutton = 1;
+ } elsif ((&Apache::lonnet::metadata($resurl,'sourceavail') eq 'open') &&
+ (&Apache::lonnet::allowed('bre','/'))) {
+ $viewsrcbutton = 1;
+ }
+ }
+ }
+ if ($viewsrcbutton) {
+ &switch('','',6,1,'pcstr.png','View Source','resource[_2]','open_source()',
+ 'View source code');
+ }
+ }
}
}
}
@@ -1515,15 +1640,20 @@ 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+/ext\.tool$}) ||
($resurl =~ m{^/uploaded/$cdom/$cnum/supplemental/}) ||
($resurl eq '/adm/supplemental') ||
($resurl =~ m{^/public/$cdom/$cnum/syllabus$}) ||
($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','','',1);
}
}
@@ -1535,7 +1665,7 @@ sub prepare_functions {
&advtools_crumbs(@inlineremote);
return $editbutton;
} elsif (($env{'request.registered'}) && (!ref($forbodytag))) {
- return $editbutton;
+ return $editbutton || $viewsrcbutton;
} else {
if (ref($bread_crumbs) eq 'ARRAY') {
if (@inlineremote > 0) {
@@ -2017,6 +2147,217 @@ 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'});
+ 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.').'