--- loncom/interface/lonnavmaps.pm 2002/10/04 15:38:22 1.65
+++ loncom/interface/lonnavmaps.pm 2002/10/07 21:07:47 1.68
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Navigate Maps Handler
#
-# $Id: lonnavmaps.pm,v 1.65 2002/10/04 15:38:22 bowersj2 Exp $
+# $Id: lonnavmaps.pm,v 1.68 2002/10/07 21:07:47 bowersj2 Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -841,12 +841,12 @@ sub new_handle {
$r->print("
$date
\n");
$r->rflush();
if ($navmap->{LAST_CHECK}) {
- $r->print('
New discussion since '.
- timeToHumanString($navmap->{LAST_CHECK}).
- '
New message (click to open)');
+ $r->print('
New discussion since '.
+ timeToHumanString($navmap->{LAST_CHECK}).
+ '
New message (click to open)
');
} else {
- $r->print('
Discussions'.
- '
New message (click to open)
');
+ $r->print('
Discussions'.
+ '
New message (click to open)
');
}
#if (($currenturl=~/^\/res/) &&
# ($currenturl!~/^\/res\/adm/)) {
@@ -882,22 +882,22 @@ sub new_handle {
my $hurryUpColor = "#FFCCCC";
my %statusIconMap =
- ( $res->NETWORK_FAILURE => '',
- $res->NOTHING_SET => '',
- $res->CORRECT => 'navmap.correct.gif',
- $res->EXCUSED => 'navmap.correct.gif',
- $res->PAST_DUE_NO_ANSWER => 'navmap.wrong.gif',
- $res->PAST_DUE_ANSWER_LATER => 'navmap.wrong.gif',
- $res->ANSWER_OPEN => 'navmap.wrong.gif',
- $res->OPEN_LATER => '',
- $res->TRIES_LEFT => 'navmap.open.gif',
- $res->INCORRECT => 'navmap.wrong.gif',
- $res->OPEN => 'navmap.open.gif' );
+ ( $res->NETWORK_FAILURE => '',
+ $res->NOTHING_SET => '',
+ $res->CORRECT => 'navmap.correct.gif',
+ $res->EXCUSED => 'navmap.correct.gif',
+ $res->PAST_DUE_NO_ANSWER => 'navmap.wrong.gif',
+ $res->PAST_DUE_ANSWER_LATER => 'navmap.wrong.gif',
+ $res->ANSWER_OPEN => 'navmap.wrong.gif',
+ $res->OPEN_LATER => '',
+ $res->TRIES_LEFT => 'navmap.open.gif',
+ $res->INCORRECT => 'navmap.wrong.gif',
+ $res->OPEN => 'navmap.open.gif' );
my %condenseStatuses =
- ( $res->NETWORK_FAILURE => 1,
- $res->NOTHING_SET => 1,
- $res->CORRECT => 1 );
+ ( $res->NETWORK_FAILURE => 1,
+ $res->NOTHING_SET => 1,
+ $res->CORRECT => 1 );
my %filterHash;
# Figure out what we're not displaying
@@ -912,14 +912,18 @@ sub new_handle {
$currenturl=~s/^[^\/]+//;
my $queryAdd = "postdata=" . &Apache::lonnet::escape($currenturl);
- $r->print("$currenturl
");
-
+ $r->print('Show All Resources
');
# Begin the HTML table
# four cols: resource + indent, chat+feedback, icon, text string
$r->print('
' ."\n");
- my $mapIterator = $navmap->getIterator(undef, undef, \%filterHash, 0);
+ my $condition = 0;
+ if ($ENV{'form.condition'}) {
+ $condition = 1;
+ }
+
+ my $mapIterator = $navmap->getIterator(undef, undef, \%filterHash, $condition);
my $curRes = $mapIterator->next();
undef $res; # so we don't accidentally use it later
my $indentLevel = -1;
@@ -942,67 +946,74 @@ sub new_handle {
$isNewBranch = 1;
}
+ # Is this resource being blotted out?
+ if (ref($curRes) && !advancedUser() && $curRes->randomout()) {
+ $curRes = $mapIterator->next();
+ next; # and totally ignore this resource
+ }
+
if (ref($curRes) && $curRes->src()) {
- # Step one: Decide which parts to show
- my @parts = @{$curRes->parts()};
- my $multipart = scalar(@parts) > 1;
- my $condensed = 0;
-
+ # Step one: Decide which parts to show
+ my @parts = @{$curRes->parts()};
+ my $multipart = scalar(@parts) > 1;
+ my $condensed = 0;
+
if ($curRes->is_problem()) {
- # Is it multipart?
+ # Is it multipart?
- if ($multipart) {
- # If it's multipart, see if part 0 is "open"
- # if it is, display all parts, if it isn't,
- # just display first
- if (!$curRes->opendate("0")) {
- @parts = ("0"); # just display the zero-th part
- } else {
- # Otherwise, only display part 0 if we want to
- # attach feedback or email information to it
- if ($curRes->hasDiscussion() || $curRes->getFeedback()) {
- shift @parts;
- } else {
- # If there's discussion or feedback, that counts
- # as a difference, so display the parts.
-
- # Now, we decide whether to condense the
- # parts due to similarity
- my $status = $curRes->status($parts[1]);
- my $due = $curRes->duedate($parts[1]);
- my $open = $curRes->opendate($parts[1]);
- my $statusAllSame = 1;
- my $dueAllSame = 1;
- my $openAllSame = 1;
- for (my $i = 2; $i < scalar(@parts); $i++) {
- if ($curRes->status($parts[$i]) != $status){
- $statusAllSame = 0;
- }
- if ($curRes->duedate($parts[$i]) != $due ) {
- $dueAllSame = 0;
- }
- if ($curRes->opendate($parts[$i]) != $open) {
- $openAllSame = 0;
- }
- }
-
- # $allSame is true if all the statuses were
- # the same. Now, if they are all the same and
- # match one of the statuses to condense, or they
- # are all open with the same due date, or they are
- # all OPEN_LATER with the same open date, display the
- # status of the first non-zero part (to get the 'correct'
- # status right, since 0 is never 'correct' or 'open').
- if (($statusAllSame && defined($condenseStatuses{$status})) ||
- ($dueAllSame && $status == $curRes->OPEN && $statusAllSame)||
- ($openAllSame && $status == $curRes->OPEN_LATER && $statusAllSame) ){
- @parts = ($parts[1]);
- $condensed = 1;
- }
- }
- }
- }
+ if ($multipart) {
+ # If it's multipart, see if part 0 is "open"
+ # if it is, display all parts, if it isn't,
+ # just display first
+ if (!$curRes->opendate("0")) {
+ @parts = ("0"); # just display the zero-th part
+ $condensed = 1;
+ } else {
+ # Otherwise, only display part 0 if we want to
+ # attach feedback or email information to it
+ if ($curRes->hasDiscussion() || $curRes->getFeedback()) {
+ shift @parts;
+ } else {
+ # If there's discussion or feedback, that counts
+ # as a difference, so display the parts.
+
+ # Now, we decide whether to condense the
+ # parts due to similarity
+ my $status = $curRes->status($parts[1]);
+ my $due = $curRes->duedate($parts[1]);
+ my $open = $curRes->opendate($parts[1]);
+ my $statusAllSame = 1;
+ my $dueAllSame = 1;
+ my $openAllSame = 1;
+ for (my $i = 2; $i < scalar(@parts); $i++) {
+ if ($curRes->status($parts[$i]) != $status){
+ $statusAllSame = 0;
+ }
+ if ($curRes->duedate($parts[$i]) != $due ) {
+ $dueAllSame = 0;
+ }
+ if ($curRes->opendate($parts[$i]) != $open) {
+ $openAllSame = 0;
+ }
+ }
+
+ # $allSame is true if all the statuses were
+ # the same. Now, if they are all the same and
+ # match one of the statuses to condense, or they
+ # are all open with the same due date, or they are
+ # all OPEN_LATER with the same open date, display the
+ # status of the first non-zero part (to get the 'correct'
+ # status right, since 0 is never 'correct' or 'open').
+ if (($statusAllSame && defined($condenseStatuses{$status})) ||
+ ($dueAllSame && $status == $curRes->OPEN && $statusAllSame)||
+ ($openAllSame && $status == $curRes->OPEN_LATER && $statusAllSame) ){
+ @parts = ($parts[1]);
+ $condensed = 1;
+ }
+ }
+ }
+ }
} else {
@parts[0] = "0"; # this is to get past foreach loop below
@@ -1010,10 +1021,18 @@ sub new_handle {
# with only one part without loss
}
- # Step two: Show the parts
+ # Display one part, in event of network error.
+ # If this is a single part, we can at least show the correct
+ # status, but if it's multipart, we're lost.
+ if ($curRes->{RESOURCE_ERROR}) {
+ @parts = ("0");
+ }
+
+ # Step two: Show the parts
foreach my $part (@parts) {
- my $deltalevel = 0; # for inserting the branch icon
+ my $deltalevel = 0; # for inserting the branch icon
+ my $nonLinkedText = ""; # unlinked stuff after title
# For each thing we're displaying...
@@ -1034,7 +1053,7 @@ sub new_handle {
if ($isNewBranch) {
$newBranchText = "
";
$isNewBranch = 0;
- $deltalevel = 1;
+ $deltalevel = 1;
}
# links to open and close the folders
@@ -1043,11 +1062,11 @@ sub new_handle {
my $icon = "
";
if ($curRes->is_problem()) {
- if ($part eq "0" || $condensed) {
- $icon = '
';
- } else {
- $icon = $indentString;
- }
+ if ($part eq "0" || $condensed) {
+ $icon = '
';
+ } else {
+ $icon = $indentString;
+ }
}
# Display the correct icon, link to open or shut map
@@ -1056,14 +1075,14 @@ sub new_handle {
my $nowOpen = !defined($filterHash{$mapId});
$icon = $nowOpen ?
"folder_closed.gif" : "folder_opened.gif";
- $icon = "
";
+ $icon = "
";
$linkopen = "";
+ $linkopen .= "&condition=$condition&$queryAdd\">";
$linkclose = "";
-
+
}
my $colorizer = "";
@@ -1075,8 +1094,12 @@ sub new_handle {
}
}
- # FIRST ROW: The resource indentation, branch icon, and name
- $r->print(" \n");
+ if ($curRes->randomout()) {
+ $nonLinkedText .= ' (hidden) ';
+ }
+
+ # FIRST COL: The resource indentation, branch icon, and name
+ $r->print(" |
\n");
# print indentation
for (my $i = 0; $i < $indentLevel - $deltalevel; $i++) {
@@ -1086,63 +1109,74 @@ sub new_handle {
$r->print(" ${newBranchText}${linkopen}$icon${linkclose}\n");
if ($curRes->is_problem() && $part != "0" && !$condensed) {
- $partLabel = " (Part $part)";
- $title = "";
- }
-
- $r->print(" $title$partLabel | ");
-
- my $discussionHTML = ""; my $feedbackHTML = "";
-
- # SECOND ROW: Is there text or feedback?
- if ($curRes->hasDiscussion()) {
- $discussionHTML = $linkopen .
- '
' .
- $linkclose;
- }
-
- if ($curRes->getFeedback()) {
- my $feedback = $curRes->getFeedback();
- foreach (split(/\,/, $feedback)) {
- if ($_) {
- $feedbackHTML .= ' '
- . '
';
- }
- }
- }
-
- $r->print("$discussionHTML$feedbackHTML | ");
-
- # Is this the first displayed part of a multi-part problem
- # that has not been condensed, so we should suppress these two
- # columns?
- my $firstDisplayed = !$condensed && $multipart && $part eq "0";
-
- # THIRD ROW: Problem status icon
- if ($curRes->is_problem() &&
- !$firstDisplayed) {
- my $icon = $statusIconMap{$curRes->status($part)};
- if ($icon) {
- $r->print("$linkopen $linkclose | \n");
- } else {
- $r->print(" | \n");
- }
- } else { # not problem, no icon
- $r->print(" | \n");
- }
+ $partLabel = " (Part $part)";
+ $title = "";
+ }
+ if ($multipart && $condensed) {
+ $nonLinkedText .= ' (' . $curRes->countParts() . ' parts)';
+ }
+
+ $r->print(" $title$partLabel $nonLinkedText");
+
+ if ($curRes->{RESOURCE_ERROR}) {
+ $r->print(&Apache::loncommon::help_open_topic ("Navmap_Host_Down",
+ 'Host down'));
+ }
+
+ my $discussionHTML = ""; my $feedbackHTML = "";
+
+ # SECOND COL: Is there text or feedback?
+ if ($curRes->hasDiscussion()) {
+ $discussionHTML = $linkopen .
+ '
' .
+ $linkclose;
+ }
+
+ if ($curRes->getFeedback()) {
+ my $feedback = $curRes->getFeedback();
+ foreach (split(/\,/, $feedback)) {
+ if ($_) {
+ $feedbackHTML .= ' '
+ . '
';
+ }
+ }
+ }
+
+ $r->print("$discussionHTML$feedbackHTML | ");
+
+ # Is this the first displayed part of a multi-part problem
+ # that has not been condensed, so we should suppress these two
+ # columns?
+ my $firstDisplayed = !$condensed && $multipart && $part eq "0";
+
+ # THIRD ROW: Problem status icon
+ if ($curRes->is_problem() &&
+ !$firstDisplayed) {
+ my $icon = $statusIconMap{$curRes->status($part)};
+ if ($icon) {
+ $r->print("$linkopen $linkclose | \n");
+ } else {
+ $r->print(" | \n");
+ }
+ } else { # not problem, no icon
+ $r->print(" | \n");
+ }
- # FOURTH ROW: Text description
- $r->print("\n");
+ # FOURTH ROW: Text description
+ $r->print(" | \n");
if ($curRes->kind() eq "res" &&
$curRes->is_problem() &&
- !$firstDisplayed) {
+ !$firstDisplayed) {
$r->print (getDescription($curRes, $part));
}
+ if ($curRes->is_map() && advancedUser() && $curRes->randompick()) {
+ $r->print('(randomly select ' . $curRes->randompick() .')');
+ }
- $r->print(" |
\n");
+ $r->print("\n");
}
}
$curRes = $mapIterator->next();
@@ -1220,11 +1254,11 @@ sub getDescription {
return "Opens: " . timeToHumanString($res->opendate($part));
}
if ($status == $res->OPEN) {
- if ($res->duedate()) {
- return "Due: $status " . timeToHumanString($res->duedate($part));
- } else {
- return "Open, no due date";
- }
+ if ($res->duedate()) {
+ return "Due: $status " . timeToHumanString($res->duedate($part));
+ } else {
+ return "Open, no due date";
+ }
}
if ($status == $res->PAST_DUE_ANSWER_LATER) {
return "Answer opens: " . timeToHumanString($res->answerdate($part));
@@ -1236,21 +1270,25 @@ sub getDescription {
return "Answer available";
}
if ($status == $res->EXCUSED) {
- return "Excused by instructor";
+ return "Excused by instructor";
}
if ($status == $res->TRIES_LEFT) {
- my $tries = $res->tries();
- my $maxtries = $res->maxtries();
- my $triesString = "($tries of $maxtries tries used)";
- if ($res->duedate()) {
- return "Due: $status " . timeToHumanString($res->duedate($part)) .
- " $triesString";
- } else {
- return "No due date $triesString";
- }
+ my $tries = $res->tries();
+ my $maxtries = $res->maxtries();
+ my $triesString = "($tries of $maxtries tries used)";
+ if ($res->duedate()) {
+ return "Due: $status " . timeToHumanString($res->duedate($part)) .
+ " $triesString";
+ } else {
+ return "No due date $triesString";
+ }
}
}
+sub advancedUser {
+ return $ENV{'user.adv'};
+}
+
# I want to change this into something more human-friendly. For
# now, this is a simple call to localtime. The final function
# probably belongs in loncommon.
@@ -1258,9 +1296,9 @@ sub timeToHumanString {
my ($time) = @_;
# zero, '0' and blank are bad times
if ($time) {
- return localtime($time);
+ return localtime($time);
} else {
- return 'Never';
+ return 'Never';
}
}
@@ -1328,7 +1366,7 @@ sub new {
if (!(tie(%parmhash, 'GDBM_File', $self->{PARM_HASH_FILE},
&GDBM_READER(), 0640)))
{
- untie $self->{PARM_HASH};
+ untie $self->{PARM_HASH};
return undef;
}
$self->{PARM_HASH} = \%parmhash;
@@ -1481,7 +1519,7 @@ sub hasDiscussion {
if (!defined($self->{DISCUSSION_TIME})) { return 0; }
return $self->{DISCUSSION_TIME}->{$symb} >
- $self->{LAST_CHECK};
+ $self->{LAST_CHECK};
}
# Private function: Does the given resource (as a symb string) have
@@ -1748,21 +1786,33 @@ sub new {
return $self;
}
+# FIXME: Document this.
+sub cancelTopRecursion {
+ my $self = shift;
+
+ if (!$self->{RECURSIVE_ITERATOR_FLAG}) {return;}
+
+ # is this the iterator we want to kill?
+ if ($self->{RECURSIVE_ITERATOR_FLAG} &&
+ !$self->{RECURSIVE_ITERATOR}->{RECURSIVE_ITERATOR_FLAG}) {
+ $self->{RECURSIVE_ITERATOR_FLAG} = 0;
+ undef $self->{RECURSIVE_ITERATOR};
+ return;
+ }
+
+ $self->{RECURSIVE_ITERATOR}->cancelTopRecursion();
+}
+
# Note... this function is *touchy*. I strongly recommend tracing
# through it with the debugger a few times on a non-trivial map before
# modifying it. Order is *everything*.
+# FIXME: Doc that skipMap will prevent the recursion, if any.
sub next {
my $self = shift;
+ my $skipMap = shift;
# Iterator logic goes here
- # Is this return value pre-determined?
- if (defined($self->{FORCE_NEXT})) {
- my $tmp = $self->{FORCE_NEXT};
- $self->{FORCE_NEXT} = undef;
- return $tmp;
- }
-
# Are we using a recursive iterator? If so, pull from that and
# watch the depth; we want to resume our level at the correct time.
if ($self->{RECURSIVE_ITERATOR_FLAG})
@@ -1782,6 +1832,13 @@ sub next {
return $next;
}
+ # Is this return value pre-determined?
+ if (defined($self->{FORCE_NEXT})) {
+ my $tmp = $self->{FORCE_NEXT};
+ $self->{FORCE_NEXT} = undef;
+ return $tmp;
+ }
+
# Is there a current resource to grab? If not, then return
# END_BRANCH and END_MAP in succession.
if (scalar(@{$self->{BRANCH_STACK}}) == 0) {
@@ -1870,7 +1927,6 @@ sub next {
$self->{FORCE_NEXT} = $self->END_BRANCH();
$self->{BRANCH_DEPTH}--;
}
- return $self->{HERE};
}
while (@$next) {
@@ -1886,19 +1942,16 @@ sub next {
}
# If this is a map and we want to recurse down it... (not filtered out)
- if ($self->{HERE}->is_map() &&
+ if ($self->{HERE}->is_map() && !$skipMap &&
(defined($self->{FILTER}->{$self->{HERE}->map_pc()}) xor $self->{CONDITION})) {
$self->{RECURSIVE_ITERATOR_FLAG} = 1;
my $firstResource = $self->{HERE}->map_start();
my $finishResource = $self->{HERE}->map_finish();
- # Odd perl syntax here; $self->new allows one to create a new iterator
- # can't figure out how to ref this package directly correctly
- # isn't MAIN::new, __PACKAGE__::new or Apache::lonnavmaps::iterator->new
$self->{RECURSIVE_ITERATOR} =
Apache::lonnavmaps::iterator->new ($self->{NAV_MAP}, $firstResource,
$finishResource, $self->{FILTER}, $self->{ALREADY_SEEN},
- $self->{CONDITION});
+ $self->{CONDITION});
}
return $self->{HERE};
@@ -1971,6 +2024,7 @@ sub new {
# Store this new resource in the parent nav map's cache.
$self->{NAV_MAP}->{RESOURCE_CACHE}->{$self->{ID}} = $self;
+ $self->{RESOURCE_ERROR} = 0;
# A hash that can be used by two-pass algorithms to store data
# about this resource in. Not used by the resource object
@@ -2014,6 +2068,13 @@ sub goesto { my $self=shift; return $sel
# "to" can return a comma seperated list for branches
sub to { my $self=shift; return $self->navHash("to_", 1); }
sub kind { my $self=shift; return $self->navHash("kind_", 1); }
+sub ext { my $self=shift; return $self->navHash("ext_", 1) eq 'true:'; }
+sub randomout { my $self=shift; return $self->navHash("randomout_", 1); }
+sub randompick {
+ my $self = shift;
+ return $self->{NAV_MAP}->{PARM_HASH}->{$self->symb .
+ '.0.parameter_randompick'};
+}
sub src {
my $self=shift;
return $self->navHash("src_", 1);
@@ -2263,6 +2324,8 @@ sub getFeedback {
sub parts {
my $self = shift;
+ if ($self->ext) { return ['0']; }
+
$self->extractParts();
return $self->{PARTS};
}
@@ -2271,8 +2334,12 @@ sub countParts {
my $self = shift;
my $parts = $self->parts();
-
- if (scalar(@{$parts}) == 1) { return 1;}
+
+ if ($self->{RESOURCE_ERROR}) {
+ return 0;
+ }
+
+ if (scalar(@{$parts}) < 2) { return 1;}
return scalar(@{$parts}) - 1;
}
@@ -2282,16 +2349,24 @@ sub extractParts {
my $self = shift;
return if ($self->{PARTS});
+ return if ($self->ext);
$self->{PARTS} = [];
# Retrieve part count
- foreach (split(/\,/,&Apache::lonnet::metadata($self->src(),
- 'allpossiblekeys'))) {
+ my $metadata = &Apache::lonnet::metadata($self->src(), 'allpossiblekeys');
+ if (!$metadata) {
+ $self->{RESOURCE_ERROR} = 1;
+ $self->{PARTS} = [];
+ return;
+ }
+
+ foreach (split(/\,/,$metadata)) {
if ($_ =~ /^parameter\_(.*)\_opendate$/) {
push @{$self->{PARTS}}, $1;
}
}
+
# Is this possible to do in one line? - Jeremy
my @sortedParts = sort @{$self->{PARTS}};
@@ -2566,11 +2641,11 @@ sub getNext {
$next = $self->{NAV_MAP}->getById($next);
# Don't remember it if we've already seen it or if
- # the student doesn't have browse priviledges
- my $browsePriv = &Apache::lonnet::allowed('bre', $self->src);
+ # the student doesn't have browse priviledges
+ my $browsePriv = &Apache::lonnet::allowed('bre', $self->src);
if (!defined($alreadySeenHash) ||
!defined($alreadySeenHash->{$next->{ID}}) ||
- ($browsePriv ne '2' && $browsePriv ne 'F')) {
+ ($browsePriv ne '2' && $browsePriv ne 'F')) {
push @branches, $next;
}
}