--- loncom/interface/lonmenu.pm 2021/12/13 23:26:32 1.369.2.83
+++ loncom/interface/lonmenu.pm 2023/12/29 19:53:25 1.369.2.83.2.13
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Routines to control the menu
#
-# $Id: lonmenu.pm,v 1.369.2.83 2021/12/13 23:26:32 raeburn Exp $
+# $Id: lonmenu.pm,v 1.369.2.83.2.13 2023/12/29 19:53:25 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -99,7 +99,7 @@ It gets filled in the BEGIN block of thi
=over
-=item prep_menuitems(\@menuitem)
+=item prep_menuitems(\@menuitem,$target,$listclass,$linkattr)
This routine wraps a menuitem in proper HTML. It is used by primary_menu() and
secondary_menu().
@@ -240,9 +240,9 @@ use vars qw(@desklines %category_names %
my @inlineremote;
sub prep_menuitem {
- my ($menuitem,$listclass,$linkattr) = @_;
+ my ($menuitem,$target,$listclass,$linkattr) = @_;
return '' unless(ref($menuitem) eq 'ARRAY');
- my $link;
+ my ($link,$targetattr);
if ($$menuitem[1]) { # graphical Link
$link = "':'
';
}
}
@@ -741,7 +891,8 @@ sub registerurl {
}
sub innerregister {
- my ($forcereg,$bread_crumbs,$group,$pagebuttonshide,$hostname) = @_;
+ my ($forcereg,$bread_crumbs,$group,$pagebuttonshide,$hostname,
+ $ltiscope,$ltiuri,$showncrumbsref) = @_;
my $const_space = ($env{'request.state'} eq 'construct');
my $is_const_dir = 0;
@@ -782,30 +933,33 @@ sub innerregister {
my $restitle = &Apache::lonnet::gettitle($symb);
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);
+ if (($env{'request.noversionuri'} ne '/adm/navmaps') && ($mapurl ne '')) {
+ unless ($ltiscope eq 'resource') {
+ if (($mapurl ne $env{'course.'.$env{'request.course.id'}.'.url'}) &&
+ !(($ltiscope eq 'map') && (&Apache::lonnet::clutter($resurl) eq $ltiuri))) {
+ $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'})) {
+ unless (($ltiscope eq 'map') || ($ltiscope eq 'resource')) {
@crumbs = ({text => $crstype.' Contents',
href => "Javascript:gopost('/adm/navmaps','')"});
}
if ($mapurl ne $env{'course.'.$env{'request.course.id'}.'.url'}) {
if (@mapcrumbs) {
push(@crumbs,@mapcrumbs);
- } else {
+ } elsif (($ltiscope ne 'map') && ($ltiscope ne 'resource')) {
push(@crumbs, {text => '...',
no_mt => 1});
}
}
unless ((@mapcrumbs) || (!$maptitle) || ($maptitle eq 'default.sequence') ||
- ($mapurl eq $env{'course.'.$env{'request.course.id'}.'.url'})) {
+ ($mapurl eq $env{'course.'.$env{'request.course.id'}.'.url'}) ||
+ ($ltiscope eq 'resource')) {
push @crumbs, {text => $maptitle, no_mt => 1,
href => &Apache::lonnet::clutter($mapurl).'?navmap=1'};
}
@@ -834,27 +988,51 @@ sub innerregister {
if ($env{'form.title'}) {
$title = $env{'form.title'};
}
- my $trail;
+ my ($trail,$cnum,$cdom);
+ if ($env{'form.folderpath'}) {
+ $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ &Apache::loncommon::validate_folderpath(1,'',$cnum,$cdom);
+ }
if ($env{'form.folderpath'}) {
&prepare_functions($resurl,$forcereg,$group,undef,undef,1,$hostname);
+ $title = &HTML::Entities::encode($title,'\'"<>&');
($trail) =
- &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1);
+ &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1,1);
} else {
&Apache::lonhtmlcommon::add_breadcrumb(
{text => "Supplemental $crstype Content",
href => "javascript:gopost('/adm/supplemental','')"});
- $title = &mt('View Resource');
+ $title = &HTML::Entities::encode(&mt('View Resource'),'\'"<>&');
($trail) =
- &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1);
+ &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1,1);
+ }
+ if (ref($showncrumbsref)) {
+ $$showncrumbsref = 1;
}
return $trail;
} elsif ($resurl =~ m{^\Q/uploaded$courseurl/portfolio/syllabus/}) {
&Apache::lonhtmlcommon::clear_breadcrumbs();
&prepare_functions('/public'.$courseurl."/syllabus",
$forcereg,$group,undef,undef,1,$hostname);
- $title = &mt('Syllabus File');
+ $title = &HTML::Entities::encode(&mt('Syllabus File'),'\'"<>&');
+ my ($trail) =
+ &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1,1);
+ if (ref($showncrumbsref)) {
+ $$showncrumbsref = 1;
+ }
+ return $trail;
+ } elsif (($resurl eq '/public'.$courseurl.'/syllabus') &&
+ ($env{'form.folderpath'})) {
+ if ($env{'form.title'}) {
+ $title = $env{'form.title'};
+ } else {
+ $title = 'Syllabus';
+ }
+ &prepare_functions($resurl,$forcereg,$group,undef,undef,1,$hostname);
+ $title = &HTML::Entities::encode($title,'\'"<>&');
my ($trail) =
- &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1,$hostname);
+ &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1,1);
return $trail;
}
unless ($env{'request.state'} eq 'construct') {
@@ -906,8 +1084,14 @@ sub innerregister {
$perms{'mdc'} = &Apache::lonnet::allowed('mdc',$env{'request.course.id'});
$perms{'cev'} = &Apache::lonnet::allowed('cev',$env{'request.course.id'});
my @privs;
+ my $gradable_exttool;
if ($env{'request.symb'} ne '') {
- if ($env{'request.filename'}=~/$LONCAPA::assess_re/) {
+ if ($env{'request.noversionuri'} =~ m{^/adm/$cdom/$cnum/(\d+)/ext\.tool$}) {
+ if (&Apache::lonnet::EXT('resource.0.gradable') =~ /^yes$/i) {
+ $gradable_exttool = 1;
+ push(@privs,('mgr','vgr'));
+ }
+ } elsif ($env{'request.filename'}=~/$LONCAPA::assess_re/) {
push(@privs,('mgr','vgr'));
}
push(@privs,('opa','vpa'));
@@ -922,8 +1106,8 @@ sub innerregister {
#
# Determine whether or not to show Grades and Submissions buttons
#
- if ($env{'request.symb'} ne '' &&
- $env{'request.filename'}=~/$LONCAPA::assess_re/) {
+ if (($env{'request.symb'} ne '') &&
+ (($env{'request.filename'}=~/$LONCAPA::assess_re/) || ($gradable_exttool))) {
if ($perms{'mgr'}) {
$hwkadd.= &switch('','',7,2,'pgrd.png','Content Grades',
'grades[_4]',
@@ -958,6 +1142,102 @@ sub innerregister {
'Folder/Page Content');
}
# End modifiable folder/page container check
+
+#
+# Determine whether to show View As button for shortcut to display problem, answer, and submissions
+#
+
+ if (($env{'request.symb'} ne '') &&
+ ($env{'request.filename'}=~/$LONCAPA::assess_re/) &&
+ (($perms{'mgr'}) || ($perms{'vgr'}))) {
+ my ($viewas,$text,$change,$visibility,$vuname,$vudom,$vid,$leftvis,$defdom,
+ $domselector,$righticon);
+ my %lt = &Apache::lonlocal::texthash(
+ view => 'View',
+ upda => 'Update',
+ );
+ my $possdomstr = $env{'course.'.$env{'request.course.id'}.'.internal.userdomains'};
+ if ($possdomstr =~ /,/) {
+ my @possdoms = split(/,/,$possdomstr);
+ if ($env{'request.user_in_effect'} =~ /^$match_username:($match_domain)$/) {
+ $defdom = $1;
+ } elsif (grep(/^\Q$cdom\E$/,@possdoms)) {
+ $defdom = $cdom;
+ } elsif (&Apache::lonnet::domain($possdoms[0]) ne '') {
+ $defdom = $possdoms[0];
+ }
+ $domselector = &Apache::loncommon::select_dom_form($defdom,'vudom','','','',\@possdoms);
+ } elsif (($possdomstr ne '') && (&Apache::lonnet::domain($possdomstr) ne '')) {
+ if ($env{'request.user_in_effect'} =~ /^$match_username:($match_domain)$/) {
+ $defdom = $1;
+ } else {
+ $defdom = $possdomstr;
+ }
+ }
+ if ($env{'request.user_in_effect'} =~ /^($match_username):($match_domain)$/) {
+ ($vuname,$vudom) = ($1,$2);
+ unless (&Apache::lonnet::is_advanced_user($vudom,$vuname)) {
+ $vid = (&Apache::lonnet::idrget($vudom,$vuname))[1];
+ }
+ $viewas = $env{'request.user_in_effect'};
+ $text = $lt{'upda'};
+ $change = 'off';
+ $visibility = 'inline';
+ $leftvis = 'none';
+ $defdom = $vudom;
+ $righticon = '✖';
+ } else {
+ $text = $lt{'view'};
+ $change = 'on';
+ $visibility = 'none';
+ $leftvis = 'inline';
+ if ($defdom eq '') {
+ $defdom = $cdom;
+ }
+ }
+ my $sellink = &Apache::loncommon::selectstudent_link('userview','vuname','vudom','','','vuidentifier');
+ my $selscript=&Apache::loncommon::studentbrowser_javascript();
+ my $shownsymb = &HTML::Entities::encode(&Apache::lonenc::check_encrypt($env{'request.symb'}),'<>&"');
+ my $input;
+ my @items = (
+ '',
+ ''
+ );
+ if ($domselector) {
+ push(@items,$domselector);
+ $input = &mt('[_1]User:[_2] or [_3]ID:[_4] at [_5] | ',@items);
+ } else {
+ $input = &mt('[_1]Username:[_2] or [_3]ID:[_4] | ',@items).
+ '';
+ }
+ $input .= '',
+ '';
+ my $chooser = <
+►
+
+
+
+$righticon
+
+END
+ &switch('','',7,5,'viewuser.png','View As','user[_1]',
+ 'toggleViewAsUser('."'$change'".')',
+ 'View As','','','',$chooser);
+ }
+# End view as user check
+
}
# End course context
@@ -989,8 +1269,16 @@ s&6&1&list.png&Directory&dir[_1]&golist(
s&6&2&rtrv.png&Retrieve&version[_1]&gocstr('/adm/retrieve','/priv/$udom/$uname/$cleandisfn')&Retrieve old version
s&6&3&pub.png&Publish&resource[_3]&gocstr('/adm/publish','/priv/$udom/$uname/$cleandisfn')&Publish this resource
s&7&1&del.png&Delete&resource[_2]&gocstr('/adm/cfile?action=delete','/priv/$udom/$uname/$cleandisfn')&Delete this resource
+ENDMENUITEMS
+
+#
+# Print only makes sense for certain mime types
+#
+ if ($thisdisfn=~/\.(xml|html|htm|xhtml|xhtm|tex)$/ || $thisdisfn=~/$LONCAPA::assess_re/) {
+ $menuitems .= (<new();
+ if (ref($navmap)) {
+ my $mapres = $navmap->getResourceByUrl($ltiuri);
+ if (ref($mapres)) {
+ if ($navmap->isLastResource($mapres,$env{'request.symb'})) {
+ $showforw = 0;
+ }
+ if ($navmap->isFirstResource($mapres,$env{'request.symb'})) {
+ $showback = 0;
+ }
+ }
+ }
+ if ($showback) {
+ $menuitems.="
+s&2&1&back.png&&&gopost('/adm/flip','back:'+currentURL)&Previous content resource&&1";
+ }
+ if ($showforw) {
+ $menuitems.="
+s&2&3&forw.png&&&gopost('/adm/flip','forward:'+currentURL)&Next content resource&&3";
+ }
+ } else {
+ $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";
+ }
+ $menuitems .= (<';
$inlineremote[$idx] =
''.
- $pic.''.$top.' ';
+ $pic.''.$top.' '.$form;
}
# Remote
$img=~s/\.png$/\.gif/;
@@ -1859,7 +2200,8 @@ sub switch {
unless ($env{'request.state'} eq 'construct') {
push(@tools,63);
}
- if (($env{'environment.icons'} eq 'iconsonly') &&
+ if ((($env{'environment.icons'} eq 'iconsonly') ||
+ ($env{'environment.icons'} eq '') && ($env{'request.lti.login'})) &&
(grep(/^$idx$/,@tools))) {
$inlineremote[$idx] =
''.$pic.'';
@@ -2225,6 +2567,328 @@ 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.').'