--- loncom/interface/lonnavmaps.pm 2022/06/11 14:51:49 1.558
+++ loncom/interface/lonnavmaps.pm 2025/02/03 00:08:31 1.569
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Navigate Maps Handler
#
-# $Id: lonnavmaps.pm,v 1.558 2022/06/11 14:51:49 raeburn Exp $
+# $Id: lonnavmaps.pm,v 1.569 2025/02/03 00:08:31 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -549,6 +549,10 @@ my %colormap =
$resObj->EXCUSED => '#3333FF',
$resObj->PAST_DUE_ANSWER_LATER => '',
$resObj->PAST_DUE_NO_ANSWER => '',
+ $resObj->PAST_DUE_ATMPT_ANS => '',
+ $resObj->PAST_DUE_ATMPT_NOANS => '',
+ $resObj->PAST_DUE_NO_ATMT_ANS => '',
+ $resObj->PAST_DUE_NO_ATMT_NOANS => '',
$resObj->ANSWER_OPEN => '#006600',
$resObj->OPEN_LATER => '',
$resObj->TRIES_LEFT => '',
@@ -694,10 +698,10 @@ sub getDescription {
return &Apache::lonhtmlcommon::direct_parm_link(&mt("Open, no due date"),$res->symb(),'duedate',$part).$slotinfo;
}
}
- if ($status == $res->PAST_DUE_ANSWER_LATER) {
+ if (($status == $res->PAST_DUE_ANSWER_LATER) || ($status == $res->PAST_DUE_ATMPT_ANS) || ($status == $res->PAST_DUE_NO_ATMT_ANS)) {
return &mt("Answer open [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($answer,'start'),$res->symb(),'answerdate',$part));
}
- if ($status == $res->PAST_DUE_NO_ANSWER) {
+ if (($status == $res->PAST_DUE_NO_ANSWER) || ($status == $res->PAST_DUE_ATMPT_NOANS) || ($status == $res->PAST_DUE_NO_ATMT_NOANS)) {
if ($res->is_practice()) {
return &mt("Closed [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start'),$res->symb(),'answerdate,duedate',$part));
} else {
@@ -1020,8 +1024,12 @@ sub render_resource {
if ($it->{CONDITION}) {
$nowOpen = !$nowOpen;
}
-
- my $folderType = $resource->is_sequence() ? 'folder' : 'page';
+ my $folderType;
+ if (&advancedUser() && $resource->is_missing_map()) {
+ $folderType = 'none';
+ } else {
+ $folderType = $resource->is_sequence() ? 'folder' : 'page';
+ }
my $title=$resource->title;
$title=~s/\"/\&qout;/g;
if (!$params->{'resource_no_folder_link'}) {
@@ -1112,7 +1120,7 @@ sub render_resource {
}
# Decide what to display
- $result .= "$newBranchText$linkopen$icon$linkclose";
+ $result .= "$newBranchText$linkopen$icon";
my $curMarkerBegin = '';
my $curMarkerEnd = '';
@@ -1152,7 +1160,7 @@ sub render_resource {
$linkopen = "";
}
}
- $result .= "$curMarkerBegin$linkopen$title$partLabel$linkclose$curMarkerEnd$editmapLink$nonLinkedText";
+ $result .= "$curMarkerBegin$title$partLabel$curMarkerEnd$linkclose$editmapLink$nonLinkedText";
return $result;
}
@@ -1423,6 +1431,37 @@ sub render {
my $condition = 0;
if ($env{'form.condition'}) {
$condition = 1;
+ } elsif (($env{'request.deeplink.login'}) && ($env{'request.course.id'}) && (!$userCanSeeHidden)) {
+ if (!defined($navmap)) {
+ $navmap = Apache::lonnavmaps::navmap->new();
+ }
+ if (defined($navmap)) {
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ my $symb = &Apache::loncommon::symb_from_tinyurl($env{'request.deeplink.login'},$cnum,$cdom);
+ if ($symb) {
+ my $deeplink;
+ my $res = $navmap->getBySymb($symb);
+ if ($res->is_map()) {
+ my $mapname = &Apache::lonnet::declutter($res->src());
+ $mapname = &Apache::lonnet::deversion($mapname);
+ $deeplink = $navmap->get_mapparam(undef,$mapname,"0.deeplink");
+ } else {
+ $deeplink = $res->deeplink();
+ }
+ if ($deeplink ne '') {
+ if ((split(/,/,$deeplink))[1] eq 'hide') {
+ if ($res->is_map()) {
+ map { $filterHash->{$_} = 1 if $_ } split(/,/,$res->map_hierarchy());
+ } else {
+ my $mapurl = (&Apache::lonnet::decode_symb($symb))[0];
+ my $map = $navmap->getResourceByUrl($mapurl);
+ map { $filterHash->{$_} = 1 if $_ } split(/,/,$map->map_hierarchy());
+ }
+ }
+ }
+ }
+ }
}
if (!$env{'form.folderManip'} && !defined($args->{'iterator'}) && !$args->{'nocurrloc'}) {
@@ -1555,27 +1594,18 @@ sub render {
my $printKey = $args->{'printKey'};
my $printCloseAll = $args->{'printCloseAll'};
if (!defined($printCloseAll)) { $printCloseAll = 1; }
-
+
# Print key?
if ($printKey) {
- $result .= '
';
+ my $location = &Apache::loncommon::lonhttpdurl("/adm/lonMisc");
+ $result .= ''.
+ 'Key: | '.
+ ' '.
+ ' '.&mt('Unread Discussion').
+ ' | '.
+ ' '.
+ &mt('New message (click to open)').
+ ' |
'."\n";
}
if ($printCloseAll && !$args->{'resource_no_folder_link'}) {
@@ -1602,17 +1632,19 @@ sub render {
# Check for any unread discussions in all resources.
if (($args->{'caller'} eq 'navmapsdisplay') && (!$args->{'notools'})) {
+ my $markread = 'Mark all posts read';
&add_linkitem($args->{'linkitems'},'clearbubbles',
'document.clearbubbles.submit()',
- 'Mark all posts read');
+ $markread);
my $time=time;
+ my $submit = &mt($markread);
my $querystr = &HTML::Entities::encode($ENV{'QUERY_STRING'},'<>&"');
$result .= (<
+
END
}
}
- $result.='';
+ $result .= <
+
+END
}
if (($args->{'caller'} eq 'navmapsdisplay') && ($env{'request.course.id'})) {
my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
@@ -1693,11 +1727,31 @@ END
# mark as hidden for users who have $userCanSeeHidden.
# Use DFS for speed, since structure actually doesn't matter,
# except what map has what resources.
+ #
+ # To ensure the "Selected Resources from selected folder in course"
+ # printout generation option will work in sessions launched via a
+ # deep link, the value of $args->{'filterFunc'} included in the
+ # call to lonnavmaps::render() is omitted from the filter function
+ # used with the DFS Iterator when $args->{'caller'} is 'printout'.
+ #
+ # As a result $sequence->{DATA}->{HAS_VISIBLE_CHILDREN} can be
+ # set to 1 for folder(s) which include resources only accessible
+ # for sessions launched via a deep link, when the current session
+ # is of that type.
my $dfsit = Apache::lonnavmaps::DFSiterator->new($navmap,
$it->{FIRST_RESOURCE},
$it->{FINISH_RESOURCE},
{}, undef, 1);
+ my $dfsFilterFunc;
+ if ($args->{'caller'} eq 'printout') {
+ $dfsFilterFunc = sub { my $res = shift; return !$res->randomout() &&
+ ($res->deeplink($args->{'caller'}) ne 'absent') &&
+ ($res->deeplink($args->{'caller'}) ne 'grades') &&
+ !$res->deeplinkout();};
+ } else {
+ $dfsFilterFunc = $filterFunc;
+ }
my $depth = 0;
$dfsit->next();
my $curRes = $dfsit->next();
@@ -1716,8 +1770,9 @@ END
} elsif ($curRes->src()) {
# Not a sequence: if it's filtered, ignore it, otherwise
# rise up the stack and mark the sequences as having children
- if (&$filterFunc($curRes)) {
+ if (&$dfsFilterFunc($curRes)) {
for my $sequence (@{$dfsit->getStack()}) {
+ next unless ($sequence->is_map());
$sequence->{DATA}->{HAS_VISIBLE_CHILDREN} = 1;
}
}
@@ -2226,10 +2281,17 @@ sub new {
$self->{USERNAME} = shift || $env{'user.name'};
$self->{DOMAIN} = shift || $env{'user.domain'};
+ $self->{SECTION} = shift;
$self->{CODE} = shift;
- $self->{NOHIDE} = shift;
+ $self->{NOHIDE} = shift;
+ if (($self->{SECTION} eq '') && ($env{'request.course.sec'} ne '')) {
+ if (($self->{USERNAME} eq $env{'user.name'}) &&
+ ($self->{USERNAME} eq $env{'user.domain'})) {
+ $self->{SECTION} = $env{'request.course.sec'};
+ }
+ }
# Resource cache stores navmap resources as we reference them. We generate
# them on-demand so we don't pay for creating resources unless we use them.
@@ -2271,7 +2333,7 @@ sub new {
$self->{PARM_HASH} = \%parmhash;
$self->{PARM_CACHE} = {};
} else {
- $self->change_user($self->{USERNAME}, $self->{DOMAIN}, $self->{CODE}, $self->{NOHIDE});
+ $self->change_user($self->{USERNAME}, $self->{DOMAIN}, $self->{SECTION}, $self->{CODE}, $self->{NOHIDE});
}
return $self;
@@ -2282,15 +2344,17 @@ sub new {
# username/domain associated with a navmap (e.g. to navigate for someone
# else besides the current user...if sufficiently privileged.
# Parameters:
-# user - New user.
-# domain- Domain the user belongs to.
-# code - Anonymous CODE in use.
+# user - New user.
+# domain - Domain to which the user belongs.
+# section - Section to which the user belongs.
+# code - Anonymous CODE in use.
# Implicit inputs:
#
sub change_user {
my $self = shift;
$self->{USERNAME} = shift;
$self->{DOMAIN} = shift;
+ $self->{SECTION} = shift;
$self->{CODE} = shift;
$self->{NOHIDE} = shift;
@@ -2797,7 +2861,7 @@ sub parmval_real {
$self->generate_course_user_opt();
my $cid=$env{'request.course.id'};
- my $csec=$env{'request.course.sec'};
+ my $csec=$self->{SECTION};
my $cgroup='';
my @cgrps=split(/:/,$env{'request.course.groups'});
if (@cgrps > 0) {
@@ -2986,7 +3050,7 @@ sub parmval_real {
}
sub recurseup_maps {
- my ($self,$mapname) = @_;
+ my ($self,$mapname,$getsymb) = @_;
my @recurseup;
if ($mapname) {
my $res = $self->getResourceByUrl($mapname);
@@ -2994,7 +3058,11 @@ sub recurseup_maps {
my @pcs = split(/,/,$res->map_hierarchy());
shift(@pcs);
if (@pcs) {
- @recurseup = map { &Apache::lonnet::declutter($self->getByMapPc($_)->src()); } reverse(@pcs);
+ if ($getsymb) {
+ @recurseup = map { &Apache::lonnet::declutter($self->getByMapPc($_)->symb()); } reverse(@pcs);
+ } else {
+ @recurseup = map { &Apache::lonnet::declutter($self->getByMapPc($_)->src()); } reverse(@pcs);
+ }
}
}
}
@@ -3026,8 +3094,12 @@ sub recursed_crumbs {
my $pc = $map->map_pc();
next if ((!$pc) || ($pc == 1));
push(@links,$map);
- push(@revmapinfo,{'href' => $env{'request.use_absolute'}.$map->link().'?navmap=1','text' => $map->title(),'no_mt' => 1,});
- $totallength += length($map->title());
+ my $text = $map->title();
+ if ($text eq '') {
+ $text = '...';
+ }
+ push(@revmapinfo,{'href' => $env{'request.use_absolute'}.$map->link().'?navmap=1','text' => $text,'no_mt' => 1,});
+ $totallength += length($text);
}
my $numlinks = scalar(@links);
if ($numlinks) {
@@ -3039,7 +3111,11 @@ sub recursed_crumbs {
}
@revmapinfo = ();
foreach my $map (@links) {
- my $showntitle = &truncate_crumb_text($map->title(),$avg);
+ my $title = $map->title();
+ if ($title eq '') {
+ $title = '...';
+ }
+ my $showntitle = &truncate_crumb_text($title,$avg);
if ($showntitle ne '') {
push(@revmapinfo,{'href' => $env{'request.use_absolute'}.$map->link().'?navmap=1','text' => $showntitle,'no_mt' => 1,});
}
@@ -3114,7 +3190,7 @@ sub get_mapparam {
# Get the course id and section if there is one.
my $cid=$env{'request.course.id'};
- my $csec=$env{'request.course.sec'};
+ my $csec=$self->{SECTION};
my $cgroup='';
my @cgrps=split(/:/,$env{'request.course.groups'});
if (@cgrps > 0) {
@@ -3315,11 +3391,11 @@ sub getcourseparam {
my $uname = $self->{USERNAME};
my $udom = $self->{DOMAIN};
+ my $csec = $self->{SECTION};
- # Course, section, group ids come from the env:
+ # Course and group ids come from the env:
my $cid = $env{'request.course.id'};
- my $csec = $env{'request.course.sec'};
my $cgroup = ''; # Assume no group
my @cgroups = split(/:/, $env{'request.course.groups'});
@@ -4745,6 +4821,11 @@ sub is_sequence {
return $self->navHash("is_map_", 1) &&
$self->navHash("map_type_" . $self->map_pc()) eq 'sequence';
}
+sub is_missing_map {
+ my $self=shift;
+ return $self->navHash("is_map_", 1) &&
+ $self->navHash("map_type_" . $self->map_pc()) eq 'none';
+}
sub is_survey {
my $self = shift();
my $part = shift();
@@ -5172,7 +5253,7 @@ sub weight {
my $weight = &Apache::lonnet::EXT('resource.'.$part.'.weight',
$self->{SYMB}, $self->{DOMAIN},
$self->{USERNAME},
- $env{'request.course.sec'});
+ $self->{SECTION});
return $weight;
}
sub part_display {
@@ -5646,7 +5727,6 @@ The problem will be opened later.
Open and not yet due.
-
=item * B:
The due date has passed, but the answer date has not yet arrived.
@@ -5659,6 +5739,26 @@ The due date has passed and there is no
The answer date is here.
+=item * B:
+
+No dates have been set for this problem at all.
+
+=item * B:
+
+The due date has passed, feedback is suppressed, the problem was attempted, and the answer date has not yet arrived.
+
+=item * B:
+
+The due date has passed, feedback is suppressed, the problem was attempted, and there is no answer opening date set.
+
+=item * B:
+
+The due date has passed, feedback is suppressed, the problem was not attempted, and the answer date has not yet arrived.
+
+=item * B:
+
+The due date has passed, feedback is suppressed, the problem was not attempted, and there is no answer opening date set.
+
=item * B:
The information is unknown due to network failure.
@@ -5674,6 +5774,10 @@ sub PAST_DUE_NO_ANSWER { return 2; }
sub PAST_DUE_ANSWER_LATER { return 3; }
sub ANSWER_OPEN { return 4; }
sub NOTHING_SET { return 5; }
+sub PAST_DUE_ATMPT_ANS { return 6; }
+sub PAST_DUE_ATMPT_NOANS { return 7; }
+sub PAST_DUE_NO_ATMT_ANS { return 8; }
+sub PAST_DUE_NO_ATMT_NOANS { return 9; }
sub NETWORK_FAILURE { return 100; }
# getDateStatus gets the date status for a given problem part.
@@ -5877,6 +5981,26 @@ set.
The problem is past due, not considered correct, and an answer date in
the future is set.
+=item * B:
+
+The problem is past due, feedback is suppressed, the problem was
+attempted and an answer date in the future is set.
+
+=item * B:
+
+The problem is past due, feedback is suppressed, the problem was
+attempted and no answer date is set.
+
+=item * B:
+
+The problem is past due, feedback is suppressed, the problem was
+not attempted and an answer date in the future is set.
+
+=item * B:
+
+The problem is past due, feedback is suppressed, the problem was
+not attempted and no answer date is set.
+
=item * B:
The problem is past due, not correct, and the answer is now available.
@@ -5960,7 +6084,18 @@ sub status {
if ($completionStatus == CORRECT ||
$completionStatus == CORRECT_BY_OVERRIDE ||
$completionStatus == CORRECT_BY_PASSBACK ) {
- if ( $suppressFeedback ) { return ANSWER_SUBMITTED }
+ if ( $suppressFeedback ) {
+ if ($dateStatus == PAST_DUE_ANSWER_LATER ||
+ $dateStatus == PAST_DUE_NO_ANSWER ) {
+ if ($dateStatus == PAST_DUE_ANSWER_LATER) {
+ return PAST_DUE_ATMPT_ANS;
+ } else {
+ return PAST_DUE_ATMPT_NOANS;
+ }
+ } else {
+ return ANSWER_SUBMITTED;
+ }
+ }
my $awarded=$self->awarded($part);
if ($awarded < 1 && $awarded > 0) {
return PARTIALLY_CORRECT;
@@ -6000,7 +6135,23 @@ sub status {
if ($dateStatus == PAST_DUE_ANSWER_LATER ||
$dateStatus == PAST_DUE_NO_ANSWER ) {
- return $suppressFeedback ? ANSWER_SUBMITTED : $dateStatus;
+ if ($suppressFeedback) {
+ if ($completionStatus == NOT_ATTEMPTED) {
+ if ($dateStatus == PAST_DUE_ANSWER_LATER) {
+ return PAST_DUE_NO_ATMT_ANS;
+ } else {
+ return PAST_DUE_NO_ATMT_NOANS;
+ }
+ } else {
+ if ($dateStatus == PAST_DUE_ANSWER_LATER) {
+ return PAST_DUE_ATMPT_ANS;
+ } else {
+ return PAST_DUE_ATMPT_NOANS;
+ }
+ }
+ } else {
+ return $dateStatus;
+ }
}
if ($dateStatus == ANSWER_OPEN) {
@@ -6246,6 +6397,10 @@ my %compositeToSimple =
EXCUSED() => CORRECT,
PAST_DUE_NO_ANSWER() => INCORRECT,
PAST_DUE_ANSWER_LATER() => INCORRECT,
+ PAST_DUE_ATMPT_ANS() => ATTEMPTED,
+ PAST_DUE_ATMPT_NOANS() => ATTEMPTED,
+ PAST_DUE_NO_ATMT_ANS() => CLOSED,
+ PAST_DUE_NO_ATMT_NOANS() => CLOSED,
ANSWER_OPEN() => INCORRECT,
OPEN_LATER() => CLOSED,
TRIES_LEFT() => OPEN,