--- loncom/interface/lonnavmaps.pm 2017/09/03 18:52:27 1.534
+++ loncom/interface/lonnavmaps.pm 2018/01/15 00:51:42 1.540
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Navigate Maps Handler
#
-# $Id: lonnavmaps.pm,v 1.534 2017/09/03 18:52:27 raeburn Exp $
+# $Id: lonnavmaps.pm,v 1.540 2018/01/15 00:51:42 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -501,7 +501,7 @@ use Apache::lonlocal;
use Apache::lonnet;
use Apache::lonmap;
-use POSIX qw (floor strftime);
+use POSIX qw (ceil floor strftime);
use Time::HiRes qw( gettimeofday tv_interval );
use LONCAPA;
use DateTime();
@@ -658,6 +658,9 @@ sub getDescription {
} elsif ($slot_status == $res->RESERVABLE) {
$slotmsg = &mt('Reservable, reservations close [_1]',
timeToHumanString($slot_time,'end'));
+ } elsif ($slot_status == $res->NEEDS_CHECKIN) {
+ $slotmsg = &mt('Reserved, check-in needed - ends [_1]',
+ timeToHumanString($slot_time,'end'));
} elsif ($slot_status == $res->RESERVABLE_LATER) {
$slotmsg = &mt('Reservable, reservations open [_1]',
timeToHumanString($slot_time,'start'));
@@ -699,7 +702,17 @@ sub getDescription {
}
if (($status == $res->ANSWER_OPEN || $status == $res->PARTIALLY_CORRECT)
&& $res->handgrade($part) ne 'yes') {
- return &Apache::lonhtmlcommon::direct_parm_link(&mt("Answer available"),$res->symb(),'answerdate,duedate',$part);
+ my $msg = &mt('Answer available');
+ my $parmlist = 'answerdate,duedate';
+ if (($res->is_tool) && ($res->is_gradable())) {
+ if (($status == $res->PARTIALLY_CORRECT) && ($res->parmval('retrypartial',$part))) {
+ $msg = &mt('Grade received');
+ $parmlist = 'retrypartial';
+ } else {
+ $msg = &mt('Grade available');
+ }
+ }
+ return &Apache::lonhtmlcommon::direct_parm_link($msg,$res->symb(),$parmlist,$part);
}
if ($status == $res->EXCUSED) {
return &mt("Excused by instructor");
@@ -953,7 +966,28 @@ sub render_resource {
# links to open and close the folder
my $whitespace = $location.'/whitespace_21.gif';
- my $linkopen = "
"."";
+ my $linkopen = "
";
+ my $nomodal;
+ if (($params->{'modalLink'}) && (!$resource->is_sequence())) {
+ if ($link =~m{^(?:|/adm/wrapper)/ext/([^#]+)}) {
+ my $exturl = $1;
+ if (($ENV{'SERVER_PORT'} == 443) && ($exturl !~ /^https:/)) {
+ $nomodal = 1;
+ }
+ } elsif (($link eq "/public/$LONCAPA::match_domain/$LONCAPA::match_courseid/syllabus") &&
+ ($env{'request.course.id'}) && ($ENV{'SERVER_PORT'} == 443) &&
+ ($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'} =~ m{^http://})) {
+ $nomodal = 1;
+ }
+ my $esclink = &js_escape($link);
+ if ($nomodal) {
+ $linkopen .= "";
+ } else {
+ $linkopen .= "";
+ }
+ } else {
+ $linkopen .= "";
+ }
my $linkclose = "";
# Default icon: unknown page
@@ -1081,10 +1115,19 @@ sub render_resource {
}
if (!$params->{'resource_nolink'} && !$resource->is_sequence() && !$resource->is_empty_sequence) {
- $result .= "$curMarkerBegin$title$partLabel$curMarkerEnd$editmapLink$nonLinkedText";
- } else {
- $result .= "$curMarkerBegin$linkopen$title$partLabel$curMarkerEnd$editmapLink$nonLinkedText";
+ $linkclose = '';
+ if ($params->{'modalLink'}) {
+ my $esclink = &js_escape($link);
+ if ($nomodal) {
+ $linkopen = "";
+ } else {
+ $linkopen = "";
+ }
+ } else {
+ $linkopen = "";
+ }
}
+ $result .= "$curMarkerBegin$linkopen$title$partLabel$linkclose$curMarkerEnd$editmapLink$nonLinkedText";
return $result;
}
@@ -1146,7 +1189,7 @@ sub render_quick_status {
my $linkclose = "";
$result .= '
';
- if ($resource->is_problem() &&
+ if ($resource->is_gradable() &&
!$firstDisplayed) {
my $icon = $statusIconMap{$resource->simpleStatus($part)};
my $alt = $iconAltTags{$icon};
@@ -1171,7 +1214,7 @@ sub render_long_status {
my $color;
my $info = '';
- if ($resource->is_problem() || $resource->is_practice()) {
+ if ($resource->is_gradable() || $resource->is_practice()) {
$color = $colormap{$resource->status};
if (dueInLessThan24Hours($resource, $part)) {
@@ -1185,9 +1228,9 @@ sub render_long_status {
}
}
}
-
- if ($resource->kind() eq "res" &&
- $resource->is_raw_problem() &&
+
+ if (($resource->kind() eq "res") &&
+ ($resource->is_raw_problem() || $resource->is_gradable()) &&
!$firstDisplayed) {
if ($color) {$result .= ''; }
$result .= getDescription($resource, $part);
@@ -1234,7 +1277,7 @@ my @statuses = ($resObj->CORRECT, $resOb
sub render_parts_summary_status {
my ($resource, $part, $params) = @_;
- if (!$resource->is_problem() && !$resource->contains_problem) { return ' | | '; }
+ if (!$resource->is_gradable() && !$resource->contains_problem) { return ' | '; }
if ($params->{showParts}) {
return ' | ';
}
@@ -1722,6 +1765,11 @@ END
$cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
}
+ my $inhibitmenu;
+ if ($args->{'modalLink'}) {
+ $inhibitmenu = '&inhibitmenu=yes';
+ }
+
while (1) {
if ($args->{'sort'}) {
$curRes = shift(@resources);
@@ -1875,7 +1923,7 @@ END
} else {
$args->{"resourceLink"} = $src.
($srcHasQuestion?'&':'?') .
- 'symb=' . &escape($symb).$anchor;
+ 'symb=' . &escape($symb).$inhibitmenu.$anchor;
}
}
# Now, we've decided what parts to show. Loop through them and
@@ -2290,7 +2338,7 @@ sub generate_email_discuss_status {
foreach my $msgid (@keys) {
if ((!$emailstatus{$msgid}) || ($emailstatus{$msgid} eq 'new')) {
my ($sendtime,$shortsubj,$fromname,$fromdomain,$status,$fromcid,
- $symb,$error) = &Apache::lonmsg::unpackmsgid($msgid);
+ $symb,$error) = &Apache::lonmsg::unpackmsgid(&LONCAPA::escape($msgid));
&Apache::lonenc::check_decrypt(\$symb);
if (($fromcid ne '') && ($fromcid ne $cid)) {
next;
@@ -2696,6 +2744,10 @@ sub parmval_real {
my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb);
$mapname = &Apache::lonnet::deversion($mapname);
+ my $toolsymb = '';
+ if ($fn =~ /ext\.tool$/) {
+ $toolsymb = $symb;
+ }
my ($recursed,@recurseup);
# ----------------------------------------------------- Cascading lookup scheme
@@ -2816,9 +2868,9 @@ sub parmval_real {
my $meta_rwhat=$rwhat;
$meta_rwhat=~s/\./_/g;
- my $default=&Apache::lonnet::metadata($fn,$meta_rwhat);
+ my $default=&Apache::lonnet::metadata($fn,$meta_rwhat,$toolsymb);
if (defined($default)) { return [$default,'resource']}
- $default=&Apache::lonnet::metadata($fn,'parameter_'.$meta_rwhat);
+ $default=&Apache::lonnet::metadata($fn,'parameter_'.$meta_rwhat,$toolsymb);
if (defined($default)) { return [$default,'resource']}
# --------------------------------------------------- fifth, check more course
if (defined($courseopt)) {
@@ -2860,7 +2912,7 @@ sub parmval_real {
if (defined($partgeneral[0])) { return \@partgeneral; }
}
if ($recurse) { return []; }
- my $pack_def=&Apache::lonnet::packages_tab_default($fn,'resource.'.$rwhat);
+ my $pack_def=&Apache::lonnet::packages_tab_default($fn,'resource.'.$rwhat,$toolsymb);
if (defined($pack_def)) { return [$pack_def,'resource']; }
return [''];
}
@@ -2882,21 +2934,90 @@ sub recurseup_maps {
}
sub recursed_crumbs {
- my ($self,$mapurl) = @_;
+ my ($self,$mapurl,$restitle) = @_;
my (@revmapinfo,@revmapres);
my $mapres = $self->getResourceByUrl($mapurl);
if (ref($mapres)) {
@revmapres = map { $self->getByMapPc($_); } split(/,/,$mapres->map_breadcrumbs());
shift(@revmapres);
}
+ my $allowedlength = 60;
+ my $minlength = 5;
+ my $allowedtitle = 30;
+ if (($env{'environment.icons'} eq 'iconsonly') && (!$env{'browser.mobile'})) {
+ $allowedlength = 100;
+ $allowedtitle = 70;
+ }
+ if (length($restitle) > $allowedtitle) {
+ $restitle = &truncate_crumb_text($restitle,$allowedtitle);
+ }
+ my $totallength = length($restitle);
+ my @links;
+
foreach my $map (@revmapres) {
my $pc = $map->map_pc();
next if ((!$pc) || ($pc == 1));
+ push(@links,$map);
push(@revmapinfo,{'href' => $map->link().'?navmap=1','text' => $map->title(),'no_mt' => 1,});
+ $totallength += length($map->title());
+ }
+ my $numlinks = scalar(@links);
+ if ($numlinks) {
+ if ($totallength - $allowedlength > 0) {
+ my $available = $allowedlength - length($restitle);
+ my $avg = POSIX::ceil($available/$numlinks);
+ if ($avg < $minlength) {
+ $avg = $minlength;
+ }
+ @revmapinfo = ();
+ foreach my $map (@links) {
+ my $showntitle = &truncate_crumb_text($map->title(),$avg);
+ if ($showntitle ne '') {
+ push(@revmapinfo,{'href' => $map->link().'?navmap=1','text' => $showntitle,'no_mt' => 1,});
+ }
+ }
+ }
+ }
+ if ($restitle ne '') {
+ push(@revmapinfo,{'text' => $restitle, 'no_mt' => 1});
}
return @revmapinfo;
}
+sub truncate_crumb_text {
+ my ($title,$limit) = @_;
+ my $showntitle = '';
+ if (length($title) > $limit) {
+ my @words = split(/\b\s*/,$title);
+ if (@words == 1) {
+ $showntitle = substr($title,0,$limit).' ...';
+ } else {
+ my $linklength = 0;
+ my $num = 0;
+ foreach my $word (@words) {
+ $linklength += 1+length($word);
+ if ($word eq '-') {
+ $showntitle =~ s/ $//;
+ $showntitle .= $word;
+ } elsif ($linklength > $limit) {
+ if ($num < @words) {
+ $showntitle .= $word.' ...';
+ last;
+ } else {
+ $showntitle .= $word;
+ }
+ } else {
+ $showntitle .= $word.' ';
+ }
+ }
+ $showntitle =~ s/ $//;
+ }
+ return $showntitle;
+ } else {
+ return $title;
+ }
+}
+
#
# Determines the open/close dates for printing a map that
# encloses a resource.
@@ -4406,6 +4527,19 @@ sub is_problem {
}
return 0;
}
+sub is_tool {
+ my $self=shift;
+ my $src = $self->src();
+ return ($src =~ /ext\.tool$/);
+}
+sub is_gradable {
+ my $self=shift;
+ my $src = $self->src();
+ if (($src =~ /$LONCAPA::assess_re/) ||
+ (($self->is_tool()) && ($self->parmval('gradable',0) =~ /^yes$/i))) {
+ return !($self->is_practice());
+ }
+}
#
# The has below is the set of status that are considered 'incomplete'
#
@@ -5067,6 +5201,8 @@ sub parts {
my $self = shift;
if ($self->ext) { return []; }
+ if (($self->is_tool()) &&
+ ($self->is_gradable())) { return ['0']; }
$self->extractParts();
return $self->{PARTS};
@@ -5441,6 +5577,14 @@ Attempted, and not yet graded.
Attempted, and credit received for attempt (survey and anonymous survey only).
+=item * B:
+
+Attempted, but wrong for LTI Tool Provider by passback of grade
+
+=item * B:
+
+Correct for LTI Tool Provider by passback of grade
+
=back
=cut
@@ -5453,6 +5597,8 @@ sub CORRECT_BY_OVERRIDE { return 14; }
sub EXCUSED { return 15; }
sub ATTEMPTED { return 16; }
sub CREDIT_ATTEMPTED { return 17; }
+sub INCORRECT_BY_PASSBACK { return 18; }
+sub CORRECT_BY_PASSBACK { return 19; }
sub getCompletionStatus {
my $self = shift;
@@ -5467,8 +5613,12 @@ sub getCompletionStatus {
if ($status eq 'correct_by_override') {
return $self->CORRECT_BY_OVERRIDE;
}
+ if ($status eq 'correct_by_passback') {
+ return $self->CORRECT_BY_PASSBACK;
+ }
if ($status eq 'incorrect_attempted') {return $self->INCORRECT; }
if ($status eq 'incorrect_by_override') {return $self->INCORRECT_BY_OVERRIDE; }
+ if ($status eq 'incorrect_by_passback') {return $self->INCORRECT_BY_PASSBACK; }
if ($status eq 'excused') {return $self->EXCUSED; }
if ($status eq 'ungraded_attempted') {return $self->ATTEMPTED; }
if ($status eq 'credit_attempted') {
@@ -5622,7 +5772,8 @@ sub status {
# There are a few whole rows we can dispose of:
if ($completionStatus == CORRECT ||
- $completionStatus == CORRECT_BY_OVERRIDE ) {
+ $completionStatus == CORRECT_BY_OVERRIDE ||
+ $completionStatus == CORRECT_BY_PASSBACK ) {
if ( $suppressFeedback ) { return ANSWER_SUBMITTED }
my $awarded=$self->awarded($part);
if ($awarded < 1 && $awarded > 0) {
@@ -5635,7 +5786,8 @@ sub status {
# If it's WRONG... and not open
if ( ($completionStatus == INCORRECT ||
- $completionStatus == INCORRECT_BY_OVERRIDE)
+ $completionStatus == INCORRECT_BY_OVERRIDE ||
+ $completionStatus == INCORRECT_BY_PASSBACK)
&& (!$self->opendate($part) || $self->opendate($part) > time()) ) {
return INCORRECT;
}
@@ -5677,7 +5829,8 @@ sub status {
}
# If it's WRONG...
- if ($completionStatus == INCORRECT || $completionStatus == INCORRECT_BY_OVERRIDE) {
+ if ($completionStatus == INCORRECT || $completionStatus == INCORRECT_BY_OVERRIDE ||
+ $completionStatus == INCORRECT_BY_PASSBACK) {
# and there are TRIES LEFT:
if ($self->tries($part) < $self->maxtries($part) || !$self->maxtries($part)) {
return $suppressFeedback ? ANSWER_SUBMITTED : TRIES_LEFT;
@@ -5731,7 +5884,7 @@ sub check_for_slot {
($checkedin,$checkedinslot) = $self->checkedin();
unless ((grep(/^\Q$checkedin\E/,@proctors)) &&
($checkedinslot eq $slot_name)) {
- return (NEEDS_CHECKIN,undef,$slot_name);
+ return (NEEDS_CHECKIN,$end,$slot_name);
}
}
return (RESERVED,$end,$slot_name);