--- loncom/interface/lonnavmaps.pm 2005/02/17 08:29:43 1.316
+++ loncom/interface/lonnavmaps.pm 2005/12/02 23:06:02 1.352
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Navigate Maps Handler
#
-# $Id: lonnavmaps.pm,v 1.316 2005/02/17 08:29:43 albertel Exp $
+# $Id: lonnavmaps.pm,v 1.352 2005/12/02 23:06:02 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -30,6 +30,7 @@
package Apache::lonnavmaps;
use strict;
+use GDBM_File;
use Apache::Constants qw(:common :http);
use Apache::loncommon();
use Apache::lonmenu();
@@ -56,6 +57,7 @@ my %statusIconMap =
$resObj->CLOSED => '',
$resObj->OPEN => 'navmap.open.gif',
$resObj->CORRECT => 'navmap.correct.gif',
+ $resObj->PARTIALLY_CORRECT => 'navmap.ellipsis.gif',
$resObj->INCORRECT => 'navmap.wrong.gif',
$resObj->ATTEMPTED => 'navmap.ellipsis.gif',
$resObj->ERROR => ''
@@ -80,21 +82,28 @@ my %colormap =
$resObj->OPEN => '',
$resObj->NOTHING_SET => '',
$resObj->ATTEMPTED => '',
- $resObj->ANSWER_SUBMITTED => ''
+ $resObj->ANSWER_SUBMITTED => '',
+ $resObj->PARTIALLY_CORRECT => '#006600'
);
# And a special case in the nav map; what to do when the assignment
# is not yet done and due in less then 24 hours
my $hurryUpColor = "#FF0000";
sub launch_win {
- my ($mode,$script,$toplinkitems)=@_;
+ my ($mode,$script,$toplinkitems,$firsttime)=@_;
my $result;
if ($script ne 'no') {
$result.='
+MENU
+ }
+ }
+ if ($ENV{QUERY_STRING} eq 'turningOffExternal') {
+ $env{'environment.remotenavmap'}='off';
}
# Create the nav map
@@ -212,9 +235,10 @@ ENDSUBM
if (!defined($navmap)) {
my $requrl = $r->uri;
- $ENV{'user.error.msg'} = "$requrl:bre:0:0:Course not initialized";
+ $env{'user.error.msg'} = "$requrl:bre:0:0:Course not initialized";
return HTTP_NOT_ACCEPTABLE;
}
+ $r->send_http_header;
my $html=&Apache::lonxml::xmlbegin();
$r->print("$html
\n");
$r->print("".&mt('Navigate Course Contents')."");
@@ -225,7 +249,7 @@ ENDSUBM
my $addentries='';
my $more_unload;
my $body_only='';
- if ($ENV{'environment.remotenavmap'} eq 'on') {
+ if ($env{'environment.remotenavmap'} eq 'on') {
$r->print('');
$r->rflush();
@@ -279,15 +303,15 @@ ENDSUBM
if ($sequenceCount == 1) {
# The automatic iterator creation in the render call
# will pick this up. We know the condition because
- # the defined($ENV{'form.filter'}) also ensures this
+ # the defined($env{'form.filter'}) also ensures this
# is a fresh call.
- $ENV{'form.filter'} = "$sequenceId";
+ $env{'form.filter'} = "$sequenceId";
}
}
if ($ENV{QUERY_STRING} eq 'launchExternal') {
$r->print('
- ');
$r->print('
@@ -296,10 +320,10 @@ ENDSUBM
');
}
- if ($ENV{'environment.remotenavmap'} ne 'on') {
+ if ($env{'environment.remotenavmap'} ne 'on') {
$r->print(&launch_win('link','yes',\%toplinkitems));
}
- if ($ENV{'environment.remotenavmap'} eq 'on') {
+ if ($env{'environment.remotenavmap'} eq 'on') {
&add_linkitem(\%toplinkitems,'closenav','collapse()',
"Close navigation window");
}
@@ -326,10 +350,10 @@ ENDSUBM
pop @$stack; # last resource in the stack is the problem
# itself, which we don't need in the map stack
my @mapPcs = map {$_->map_pc()} @$stack;
- $ENV{'form.filter'} = join(',', @mapPcs);
+ $env{'form.filter'} = join(',', @mapPcs);
# Mark as both "here" and "jump"
- $ENV{'form.postsymb'} = $curRes->symb();
+ $env{'form.postsymb'} = $curRes->symb();
}
}
}
@@ -350,30 +374,30 @@ ENDSUBM
# Display only due homework.
my $showOnlyHomework = 0;
- if ($ENV{'form.showOnlyHomework'} eq "1") {
+ if ($env{'form.showOnlyHomework'} eq "1") {
$showOnlyHomework = 1;
$suppressEmptySequences = 1;
$filterFunc = sub { my $res = shift;
return $res->completable() || $res->is_map();
};
&add_linkitem(\%toplinkitems,'everything',
- 'location.href="navmaps?sort='.$ENV{'form.sort'}.'"',
+ 'location.href="navmaps?sort='.$env{'form.sort'}.'"',
"Show Everything");
$r->print("".&mt("Uncompleted Homework")."
");
- $ENV{'form.filter'} = '';
- $ENV{'form.condition'} = 1;
+ $env{'form.filter'} = '';
+ $env{'form.condition'} = 1;
$resource_no_folder_link = 1;
} else {
&add_linkitem(\%toplinkitems,'uncompleted',
- 'location.href="navmaps?sort='.$ENV{'form.sort'}.
+ 'location.href="navmaps?sort='.$env{'form.sort'}.
'&showOnlyHomework=1"',
"Show Only Uncompleted Homework");
}
- my %selected=($ENV{'form.sort'} => 'selected=on');
+ my %selected=($env{'form.sort'} => 'selected=on');
my $sort_html=("");
# renderer call
my $renderArgs = { 'cols' => [0,1,2,3],
- 'sort' => $ENV{'form.sort'},
+ 'sort' => $env{'form.sort'},
'url' => '/adm/navmaps',
'navmap' => $navmap,
'suppressNavmap' => 1,
@@ -491,22 +515,22 @@ sub getDescription {
return &mt("Not currently assigned.");
}
if ($status == $res->OPEN_LATER) {
- return "Open " . timeToHumanString($res->opendate($part));
+ return "Open " . timeToHumanString($res->opendate($part),'start');
}
if ($status == $res->OPEN) {
if ($res->duedate($part)) {
- return &mt("Due")." " .timeToHumanString($res->duedate($part));
+ return &mt("Due")." " .timeToHumanString($res->duedate($part),'end');
} else {
return &mt("Open, no due date");
}
}
if ($status == $res->PAST_DUE_ANSWER_LATER) {
- return &mt("Answer open")." " . timeToHumanString($res->answerdate($part));
+ return &mt("Answer open")." " . timeToHumanString($res->answerdate($part),'start');
}
if ($status == $res->PAST_DUE_NO_ANSWER) {
- return &mt("Was due")." " . timeToHumanString($res->duedate($part));
+ return &mt("Was due")." " . timeToHumanString($res->duedate($part),'end');
}
- if ($status == $res->ANSWER_OPEN) {
+ if ($status == $res->ANSWER_OPEN || $status == $res->PARTIALLY_CORRECT) {
return &mt("Answer available");
}
if ($status == $res->EXCUSED) {
@@ -526,7 +550,7 @@ sub getDescription {
}
}
if ($res->duedate($part)) {
- return &mt("Due")." " . timeToHumanString($res->duedate($part)) .
+ return &mt("Due")." " . timeToHumanString($res->duedate($part),'end') .
" $triesString";
} else {
return &mt("No due date")." $triesString";
@@ -564,10 +588,10 @@ sub lastTry {
$res->duedate($part) > time();
}
-# This puts a human-readable name on the ENV variable.
+# This puts a human-readable name on the env variable.
sub advancedUser {
- return $ENV{'request.role.adv'};
+ return $env{'request.role.adv'};
}
@@ -579,8 +603,11 @@ sub advancedUser {
# print "Answer available $timestring"
# Very, very, very, VERY English-only... goodness help a localizer on
# this func...
+
+
sub timeToHumanString {
- my ($time) = @_;
+ my ($time,$type,$format) = @_;
+
# zero, '0' and blank are bad times
if (!$time) {
return &mt('never');
@@ -651,30 +678,44 @@ sub timeToHumanString {
return "$prefix$hourString$minuteString$tense";
}
+ # If there's a caller supplied format, use it.
+
+ if($format ne '') {
+ my $timeStr = strftime($format, localtime($time));
+ return $timeStr.&Apache::lonlocal::gettimezone();
+ }
+
# Less then 5 days away, display day of the week and
# HH:MM
+
if ( $delta < $day * 5 ) {
my $timeStr = strftime("%A, %b %e at %I:%M %P", localtime($time));
$timeStr =~ s/12:00 am/00:00/;
$timeStr =~ s/12:00 pm/noon/;
- return ($inPast ? "last " : "next ") .
- $timeStr;
+ return ($inPast ? "last " : "this ") .
+ $timeStr.&Apache::lonlocal::gettimezone();
}
+ my $conjunction='on';
+ if ($type eq 'start') {
+ $conjunction='at';
+ } elsif ($type eq 'end') {
+ $conjunction='by';
+ }
# Is it this year?
if ( $time[5] == $now[5]) {
# Return on Month Day, HH:MM meridian
- my $timeStr = strftime("on %A, %b %e at %I:%M %P", localtime($time));
+ my $timeStr = strftime("$conjunction %A, %b %e at %I:%M %P", localtime($time));
$timeStr =~ s/12:00 am/00:00/;
$timeStr =~ s/12:00 pm/noon/;
- return $timeStr;
+ return $timeStr.&Apache::lonlocal::gettimezone();
}
# Not this year, so show the year
- my $timeStr = strftime("on %A, %b %e %Y at %I:%M %P", localtime($time));
+ my $timeStr = strftime("$conjunction %A, %b %e %Y at %I:%M %P", localtime($time));
$timeStr =~ s/12:00 am/00:00/;
$timeStr =~ s/12:00 pm/noon/;
- return $timeStr;
+ return $timeStr.&Apache::lonlocal::gettimezone();
}
}
@@ -870,13 +911,13 @@ automatically.
=over 4
-=item * B: default: constructs one from %ENV
+=item * B: default: constructs one from %env
A reference to a fresh ::iterator to use from the navmaps. The
rendering will reflect the options passed to the iterator, so you can
use that to just render a certain part of the course, if you like. If
one is not passed, the renderer will attempt to construct one from
-ENV{'form.filter'} and ENV{'form.condition'} information, plus the
+env{'form.filter'} and env{'form.condition'} information, plus the
'iterator_map' parameter if any.
=item * B: default: not used
@@ -886,11 +927,11 @@ instruct the renderer to render only a p
the source of the map you want to process, like
'/res/103/jerf/navmap.course.sequence'.
-=item * B: default: constructs one from %ENV
+=item * B: default: constructs one from %env
A reference to a navmap, used only if an iterator is not passed in. If
this is necessary to make an iterator but it is not passed in, a new
-one will be constructed based on ENV info. This is useful to do basic
+one will be constructed based on env info. This is useful to do basic
error checking before passing it off to render.
=item * B: default: must be passed in
@@ -916,12 +957,12 @@ then only one line will be displayed for
all parts will always be displayed. If showParts is 0, this is
ignored.
-=item * B: default: determined from %ENV
+=item * B: default: determined from %env
A string identifying the URL to place the anchor 'curloc' at.
It is the responsibility of the renderer user to
ensure that the #curloc is in the URL. By default, determined through
-the use of the ENV{} 'jump' information, and should normally "just
+the use of the env{} 'jump' information, and should normally "just
work" correctly.
=item * B: default: empty string
@@ -949,7 +990,7 @@ are allowing the user to open and close
Describes the currently-open row number to cause the browser to jump
to, because the user just opened that folder. By default, pulled from
-the Jump information in the ENV{'form.*'}.
+the Jump information in the env{'form.*'}.
=item * B: default: false
@@ -1024,7 +1065,6 @@ sub render_resource {
# it will be quoted with ' in the href.
my ($left,$right) = split(/\?/, $link);
- $left =~ s/'/\\'/g;
$link = $left.'?'.$right;
my $src = $resource->src();
@@ -1038,28 +1078,28 @@ sub render_resource {
my $location=&Apache::loncommon::lonhttpdurl("/adm/lonIcons");
# If this is a new branch, label it so
if ($params->{'isNewBranch'}) {
- $newBranchText = "";
+ $newBranchText = "";
}
# links to open and close the folder
- my $linkopen = "";
+ my $linkopen = "";
my $linkclose = "";
# Default icon: unknown page
- my $icon = "";
+ my $icon = "";
if ($resource->is_problem()) {
if ($part eq '0' || $params->{'condensed'}) {
- $icon ='';
+ $icon ='';
} else {
$icon = $params->{'indentString'};
}
} else {
- $icon = "";
+ $icon = "";
}
# Display the correct map icon to open or shut map
@@ -1074,9 +1114,10 @@ sub render_resource {
if (!$params->{'resource_no_folder_link'}) {
$icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . '.gif';
- $icon = "";
+ $icon = "";
- $linkopen = "{'url'} . '?' .
$params->{'queryString'} . '&filter=';
$linkopen .= ($nowOpen xor $it->{CONDITION}) ?
addToFilter($filter, $mapId) :
@@ -1086,13 +1127,14 @@ sub render_resource {
&Apache::lonnet::escape($params->{'here'}) .
'&jump=' .
&Apache::lonnet::escape($resource->symb()) .
- "&folderManip=1'>";
+ "&folderManip=1\">";
} else {
# Don't allow users to manipulate folder
$icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') .
'.nomanip.gif';
- $icon = "";
+ $icon = "";
$linkopen = "";
$linkclose = "";
@@ -1102,6 +1144,9 @@ sub render_resource {
if ($resource->randomout()) {
$nonLinkedText .= ' (hidden) ';
}
+ if (!$resource->condval()) {
+ $nonLinkedText .= ' (conditionally hidden) ';
+ }
# We're done preparing and finally ready to start the rendering
my $result = "";
@@ -1124,7 +1169,7 @@ sub render_resource {
# Is this the current resource?
if (!$params->{'displayedHereMarker'} &&
$resource->symb() eq $params->{'here'} ) {
- $curMarkerBegin = '> ';
+ $curMarkerBegin = '>';
$curMarkerEnd = '<';
$params->{'displayedHereMarker'} = 1;
}
@@ -1142,11 +1187,11 @@ sub render_resource {
}
my $target;
- if ($ENV{'environment.remotenavmap'} eq 'on') {
+ if ($env{'environment.remotenavmap'} eq 'on') {
$target=' target="loncapaclient" ';
}
if (!$params->{'resource_nolink'} && !$resource->is_sequence() && !$resource->is_empty_sequence) {
- $result .= " $curMarkerBegin$title$partLabel$curMarkerEnd $nonLinkedText | ";
+ $result .= " $curMarkerBegin$title$partLabel$curMarkerEnd $nonLinkedText";
} else {
$result .= " $curMarkerBegin$title$partLabel$curMarkerEnd $nonLinkedText";
}
@@ -1159,7 +1204,11 @@ sub render_communication_status {
my $discussionHTML = ""; my $feedbackHTML = ""; my $errorHTML = "";
my $link = $params->{"resourceLink"};
- my $linkopen = "";
+ my $target;
+ if ($env{'environment.remotenavmap'} eq 'on') {
+ $target=' target="loncapaclient" ';
+ }
+ my $linkopen = "";
my $linkclose = "";
my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc");
if ($resource->hasDiscussion()) {
@@ -1172,7 +1221,7 @@ sub render_communication_status {
my $feedback = $resource->getFeedback();
foreach (split(/\,/, $feedback)) {
if ($_) {
- $feedbackHTML .= ' '
. '';
@@ -1187,7 +1236,7 @@ sub render_communication_status {
last if ($errorcount>=10); # Only output 10 bombs maximum
if ($_) {
$errorcount++;
- $errorHTML .= ' '
. '';
@@ -1209,7 +1258,11 @@ sub render_quick_status {
$params->{'multipart'} && $part eq "0";
my $link = $params->{"resourceLink"};
- my $linkopen = "";
+ my $target;
+ if ($env{'environment.remotenavmap'} eq 'on') {
+ $target=' target="loncapaclient" ';
+ }
+ my $linkopen = "";
my $linkclose = "";
if ($resource->is_problem() &&
@@ -1368,7 +1421,6 @@ sub render {
# no columns, no nav maps.
return '';
}
- my $mustCloseNavMap = 0;
my $navmap;
if (defined($args->{'navmap'})) {
$navmap = $args->{'navmap'};
@@ -1389,7 +1441,7 @@ sub render {
# marker
my $filterHash = {};
# Figure out what we're not displaying
- foreach (split(/\,/, $ENV{"form.filter"})) {
+ foreach (split(/\,/, $env{"form.filter"})) {
if ($_) {
$filterHash->{$_} = "1";
}
@@ -1409,30 +1461,43 @@ sub render {
}
my $condition = 0;
- if ($ENV{'form.condition'}) {
+ if ($env{'form.condition'}) {
$condition = 1;
}
- if (!$ENV{'form.folderManip'} && !defined($args->{'iterator'})) {
+ if (!$env{'form.folderManip'} && !defined($args->{'iterator'})) {
# Step 1: Check to see if we have a navmap
if (!defined($navmap)) {
$navmap = Apache::lonnavmaps::navmap->new();
- $mustCloseNavMap = 1;
- }
+ if (!defined($navmap)) {
+ # no londer in course
+ return 'No course selected
+ Select a course
';
+ }
+ }
# Step two: Locate what kind of here marker is necessary
# Determine where the "here" marker is and where the screen jumps to.
- if ($ENV{'form.postsymb'}) {
- $here = $jump = &Apache::lonnet::symbclean($ENV{'form.postsymb'});
- } elsif ($ENV{'form.postdata'}) {
+ if ($env{'form.postsymb'} ne '') {
+ $here = $jump = &Apache::lonnet::symbclean($env{'form.postsymb'});
+ } elsif ($env{'form.postdata'} ne '') {
# couldn't find a symb, is there a URL?
- my $currenturl = $ENV{'form.postdata'};
+ my $currenturl = $env{'form.postdata'};
#$currenturl=~s/^http\:\/\///;
#$currenturl=~s/^[^\/]+//;
$here = $jump = &Apache::lonnet::symbread($currenturl);
- }
+ }
+ if ($here eq '') {
+ my $last;
+ if (tie(my %hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db',
+ &GDBM_READER(),0640)) {
+ $last=$hash{'last_known'};
+ untie(%hash);
+ }
+ if ($last) { $here = $jump = $last; }
+ }
# Step three: Ensure the folders are open
my $mapIterator = $navmap->getIterator(undef, undef, undef, 1);
@@ -1459,21 +1524,20 @@ sub render {
}
}
- if ( !defined($args->{'iterator'}) && $ENV{'form.folderManip'} ) { # we came from a user's manipulation of the nav page
+ if ( !defined($args->{'iterator'}) && $env{'form.folderManip'} ) { # we came from a user's manipulation of the nav page
# If this is a click on a folder or something, we want to preserve the "here"
# from the querystring, and get the new "jump" marker
- $here = $ENV{'form.here'};
- $jump = $ENV{'form.jump'};
+ $here = $env{'form.here'};
+ $jump = $env{'form.jump'};
}
my $it = $args->{'iterator'};
if (!defined($it)) {
- # Construct a default iterator based on $ENV{'form.'} information
+ # Construct a default iterator based on $env{'form.'} information
# Step 1: Check to see if we have a navmap
if (!defined($navmap)) {
$navmap = Apache::lonnavmaps::navmap->new();
- $mustCloseNavMap = 1;
}
# See if we're being passed a specific map
@@ -1579,13 +1643,7 @@ END
my @allres=$navmap->retrieveResources();
foreach my $resource (@allres) {
if ($resource->hasDiscussion()) {
- my $ressymb;
- if ($resource->symb() =~ m-(___adm/\w+/\w+)/(\d+)/bulletinboard$-) {
- $ressymb = 'bulletin___'.$2.$1.'/'.$2.'/bulletinboard';
- } else {
- $ressymb = $resource->symb();
- }
- $haveDisc .= $ressymb.':';
+ $haveDisc .= $resource->wrap_symb().':';
$totdisc ++;
}
}
@@ -1603,14 +1661,14 @@ END
if ($args->{'caller'} eq 'navmapsdisplay') {
$result .= ''.
&Apache::loncommon::help_open_menu('','Navigation Screen','Navigation_Screen','',undef,'RAT').' | ';
- if ($ENV{'environment.remotenavmap'} ne 'on') {
+ if ($env{'environment.remotenavmap'} ne 'on') {
$result .= ' | ';
} else {
$result .= '
';
}
$result.=&show_linkitems($args->{'linkitems'});
if ($args->{'sort_html'}) {
- if ($ENV{'environment.remotenavmap'} ne 'on') {
+ if ($env{'environment.remotenavmap'} ne 'on') {
$result.=' | | | '.
''.$args->{'sort_html'}.' |
';
} else {
@@ -1647,7 +1705,7 @@ END
$args->{'condensed'} = 0;
my $location=
&Apache::loncommon::lonhttpdurl("/adm/lonIcons/whitespace1.gif");
- $args->{'indentString'} = setDefault($args->{'indentString'}, "");
+ $args->{'indentString'} = setDefault($args->{'indentString'}, "");
$args->{'displayedHereMarker'} = 0;
# If we're suppressing empty sequences, look for them here. Use DFS for speed,
@@ -1800,10 +1858,6 @@ END
$args->{'multipart'} = $curRes->multipart();
if ($condenseParts) { # do the condensation
- if (!$curRes->opendate("0")) {
- @parts = ();
- $args->{'condensed'} = 1;
- }
if (!$args->{'condensed'}) {
# Decide whether to condense based on similarity
my $status = $curRes->status($parts[0]);
@@ -2061,6 +2115,7 @@ See iterator documentation below.
use strict;
use GDBM_File;
+use Apache::lonnet;
sub new {
# magic invocation to create a class instance
@@ -2080,7 +2135,7 @@ sub new {
my %navmaphash;
my %parmhash;
- my $courseFn = $ENV{"request.course.fn"};
+ my $courseFn = $env{"request.course.fn"};
if (!(tie(%navmaphash, 'GDBM_File', "${courseFn}.db",
&GDBM_READER(), 0640))) {
return undef;
@@ -2106,56 +2161,36 @@ sub generate_course_user_opt {
my $self = shift;
if ($self->{COURSE_USER_OPT_GENERATED}) { return; }
- my $uname=$ENV{'user.name'};
- my $udom=$ENV{'user.domain'};
- my $uhome=$ENV{'user.home'};
- my $cid=$ENV{'request.course.id'};
- my $chome=$ENV{'course.'.$cid.'.home'};
- my ($cdom,$cnum)=split(/\_/,$cid);
+ my $uname=$env{'user.name'};
+ my $udom=$env{'user.domain'};
+ my $cid=$env{'request.course.id'};
+ my $cdom=$env{'course.'.$cid.'.domain'};
+ my $cnum=$env{'course.'.$cid.'.num'};
- my $userprefix=$uname.'_'.$udom.'_';
-
- my %courserdatas; my %useropt; my %courseopt; my %userrdatas;
- unless ($uhome eq 'no_host') {
# ------------------------------------------------- Get coursedata (if present)
- unless ((time-$courserdatas{$cid.'.last_cache'})<240) {
- my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.
- ':resourcedata',$chome);
- # Check for network failure
- if ( $reply =~ /no.such.host/i || $reply =~ /con_lost/i) {
- $self->{NETWORK_FAILURE} = 1;
- } elsif ($reply!~/^error\:/) {
- $courserdatas{$cid}=$reply;
- $courserdatas{$cid.'.last_cache'}=time;
- }
- }
- foreach (split(/\&/,$courserdatas{$cid})) {
- my ($name,$value)=split(/\=/,$_);
- $courseopt{$userprefix.&Apache::lonnet::unescape($name)}=
- &Apache::lonnet::unescape($value);
+ my $courseopt=&Apache::lonnet::get_courseresdata($cnum,$cdom);
+ # Check for network failure
+ if (!ref($courseopt)) {
+ if ( $courseopt =~ /no.such.host/i || $courseopt =~ /con_lost/i) {
+ $self->{NETWORK_FAILURE} = 1;
}
+ undef($courseopt);
+ }
+
# --------------------------------------------------- Get userdata (if present)
- unless ((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<240) {
- my $reply=&Apache::lonnet::reply('dump:'.$udom.':'.$uname.':resourcedata',$uhome);
- if ($reply!~/^error\:/) {
- $userrdatas{$uname.'___'.$udom}=$reply;
- $userrdatas{$uname.'___'.$udom.'.last_cache'}=time;
- }
- # check to see if network failed
- elsif ( $reply=~/no.such.host/i || $reply=~/con.*lost/i )
- {
- $self->{NETWORK_FAILURE} = 1;
- }
- }
- foreach (split(/\&/,$userrdatas{$uname.'___'.$udom})) {
- my ($name,$value)=split(/\=/,$_);
- $useropt{$userprefix.&Apache::lonnet::unescape($name)}=
- &Apache::lonnet::unescape($value);
+
+ my $useropt=&Apache::lonnet::get_userresdata($uname,$udom);
+ # Check for network failure
+ if (!ref($useropt)) {
+ if ( $useropt =~ /no.such.host/i || $useropt =~ /con_lost/i) {
+ $self->{NETWORK_FAILURE} = 1;
}
- $self->{COURSE_OPT} = \%courseopt;
- $self->{USER_OPT} = \%useropt;
+ undef($useropt);
}
+ $self->{COURSE_OPT} = $courseopt;
+ $self->{USER_OPT} = $useropt;
+
$self->{COURSE_USER_OPT_GENERATED} = 1;
return;
@@ -2166,18 +2201,19 @@ sub generate_email_discuss_status {
my $symb = shift;
if ($self->{EMAIL_DISCUSS_GENERATED}) { return; }
- my $cid=$ENV{'request.course.id'};
- my ($cdom,$cnum)=split(/\_/,$cid);
+ my $cid=$env{'request.course.id'};
+ my $cdom=$env{'course.'.$cid.'.domain'};
+ my $cnum=$env{'course.'.$cid.'.num'};
my %emailstatus = &Apache::lonnet::dump('email_status');
my $logoutTime = $emailstatus{'logout'};
- my $courseLeaveTime = $emailstatus{'logout_'.$ENV{'request.course.id'}};
+ my $courseLeaveTime = $emailstatus{'logout_'.$env{'request.course.id'}};
$self->{LAST_CHECK} = (($courseLeaveTime > $logoutTime) ?
$courseLeaveTime : $logoutTime);
my %discussiontime = &Apache::lonnet::dump('discussiontimes',
$cdom, $cnum);
my %lastread = &Apache::lonnet::dump('nohist_'.$cid.'_discuss',
- $ENV{'user.domain'},$ENV{'user.name'},'lastread');
+ $env{'user.domain'},$env{'user.name'},'lastread');
my %lastreadtime = ();
foreach (keys %lastread) {
my $key = $_;
@@ -2187,19 +2223,16 @@ sub generate_email_discuss_status {
my %feedback=();
my %error=();
- my $keys = &Apache::lonnet::reply('keys:'.
- $ENV{'user.domain'}.':'.
- $ENV{'user.name'}.':nohist_email',
- $ENV{'user.home'});
+ my @keys = &Apache::lonnet::getkeys('nohist_email',$env{'user.domain'},
+ $env{'user.name'});
- foreach my $msgid (split(/\&/, $keys)) {
- $msgid=&Apache::lonnet::unescape($msgid);
+ foreach my $msgid (@keys) {
if ((!$emailstatus{$msgid}) || ($emailstatus{$msgid} eq 'new')) {
my $plain=
&Apache::lonnet::unescape(&Apache::lonnet::unescape($msgid));
- if ($plain=~/(Error|Feedback) \[([^\]]+)\]/) {
- my ($what,$url)=($1,$2);
- if ($what eq 'Error') {
+ if ($plain=~/ \[([^\]]+)\]\:/) {
+ my $url=$1;
+ if ($plain=~/\:Error \[/) {
$error{$url}.=','.$msgid;
} else {
$feedback{$url}.=','.$msgid;
@@ -2224,9 +2257,9 @@ sub get_user_data {
if ($self->{RETRIEVED_USER_DATA}) { return; }
# Retrieve performance data on problems
- my %student_data = Apache::lonnet::currentdump($ENV{'request.course.id'},
- $ENV{'user.domain'},
- $ENV{'user.name'});
+ my %student_data = Apache::lonnet::currentdump($env{'request.course.id'},
+ $env{'user.domain'},
+ $env{'user.name'});
$self->{STUDENT_DATA} = \%student_data;
$self->{RETRIEVED_USER_DATA} = 1;
@@ -2251,7 +2284,7 @@ sub navhash {
# Checks to see if coursemap is defined, matching test in old lonnavmaps
sub courseMapDefined {
my $self = shift;
- my $uri = &Apache::lonnet::clutter($ENV{'request.course.uri'});
+ my $uri = &Apache::lonnet::clutter($env{'request.course.uri'});
my $firstres = $self->navhash("map_start_$uri");
my $lastres = $self->navhash("map_finish_$uri");
@@ -2271,21 +2304,14 @@ sub getIterator {
sub hasDiscussion {
my $self = shift;
my $symb = shift;
-
$self->generate_email_discuss_status();
if (!defined($self->{DISCUSSION_TIME})) { return 0; }
#return defined($self->{DISCUSSION_TIME}->{$symb});
-# backward compatibility (bulletin boards used to be 'wrapped')
- my $ressymb = $symb;
- if ($ressymb =~ m|adm/(\w+)/(\w+)/(\d+)/bulletinboard$|) {
- unless ($ressymb =~ m|adm/wrapper/adm|) {
- $ressymb = 'bulletin___'.$3.'___adm/wrapper/adm/'.$1.'/'.$2.'/'.$3.'/bulletinboard';
- }
- }
-
+ # backward compatibility (bulletin boards used to be 'wrapped')
+ my $ressymb = $self->wrap_symb($symb);
if ( defined ( $self->{LAST_READ}->{$ressymb} ) ) {
return $self->{DISCUSSION_TIME}->{$ressymb} > $self->{LAST_READ}->{$ressymb};
} else {
@@ -2294,9 +2320,21 @@ sub hasDiscussion {
}
}
+sub wrap_symb {
+ my $self = shift;
+ my $symb = shift;
+ if ($symb =~ m-___(adm/\w+/\w+/)(\d+)(/bulletinboard)$-) {
+ unless ($symb =~ m|adm/wrapper/adm|) {
+ $symb = 'bulletin___'.$2.'___adm/wrapper/'.$1.$2.$3;
+ }
+ }
+ return $symb;
+}
+
# Private method: Does the given resource (as a symb string) have
# current feedback? Returns the string in the feedback hash, which
# will be false if it does not exist.
+
sub getFeedback {
my $self = shift;
my $symb = shift;
@@ -2392,7 +2430,7 @@ resource in the navmap.
sub firstResource {
my $self = shift;
my $firstResource = $self->navhash('map_start_' .
- &Apache::lonnet::clutter($ENV{'request.course.uri'}));
+ &Apache::lonnet::clutter($env{'request.course.uri'}));
return $self->getById($firstResource);
}
@@ -2408,7 +2446,7 @@ in the navmap.
sub finishResource {
my $self = shift;
my $firstResource = $self->navhash('map_finish_' .
- &Apache::lonnet::clutter($ENV{'request.course.uri'}));
+ &Apache::lonnet::clutter($env{'request.course.uri'}));
return $self->getById($firstResource);
}
@@ -2435,10 +2473,16 @@ sub parmval_real {
# Make sure the {USER_OPT} and {COURSE_OPT} hashes are populated
$self->generate_course_user_opt();
- my $cid=$ENV{'request.course.id'};
- my $csec=$ENV{'request.course.sec'};
- my $uname=$ENV{'user.name'};
- my $udom=$ENV{'user.domain'};
+ my $cid=$env{'request.course.id'};
+ my $csec=$env{'request.course.sec'};
+ my $cgroup='';
+ my @cgrps=split(/:/,$env{'request.course.groups'});
+ if (@cgrps > 0) {
+ @cgrps = sort(@cgrps);
+ $cgroup = $cgrps[0];
+ }
+ my $uname=$env{'user.name'};
+ my $udom=$env{'user.domain'};
unless ($symb) { return ''; }
my $result='';
@@ -2452,7 +2496,11 @@ sub parmval_real {
my $symbparm=$symb.'.'.$what;
my $mapparm=$mapname.'___(all).'.$what;
- my $usercourseprefix=$uname.'_'.$udom.'_'.$cid;
+ my $usercourseprefix=$cid;
+
+ my $grplevel=$usercourseprefix.'.['.$cgroup.'].'.$what;
+ my $grplevelr=$usercourseprefix.'.['.$cgroup.'].'.$symbparm;
+ my $grplevelm=$usercourseprefix.'.['.$cgroup.'].'.$mapparm;
my $seclevel= $usercourseprefix.'.['.$csec.'].'.$what;
my $seclevelr=$usercourseprefix.'.['.$csec.'].'.$symbparm;
@@ -2474,6 +2522,12 @@ sub parmval_real {
}
# ------------------------------------------------------- second, check course
+ if ($cgroup ne '' and defined($courseopt)) {
+ if (defined($$courseopt{$grplevelr})) { return $$courseopt{$grplevelr}; }
+ if (defined($$courseopt{$grplevelm})) { return $$courseopt{$grplevelm}; }
+ if (defined($$courseopt{$grplevel})) { return $$courseopt{$grplevel}; }
+ }
+
if ($csec and defined($courseopt)) {
if (defined($$courseopt{$seclevelr})) { return $$courseopt{$seclevelr}; }
if (defined($$courseopt{$seclevelm})) { return $$courseopt{$seclevelm}; }
@@ -2524,14 +2578,17 @@ sub parmval_real {
=pod
-=item * B(url):
+=item * B(url,multiple):
-Retrieves a resource object by URL of the resource. If passed a
-resource object, it will simply return it, so it is safe to use this
-method in code like "$res = $navmap->getResourceByUrl($res)", if
-you're not sure if $res is already an object, or just a URL. If the
-resource appears multiple times in the course, only the first instance
-will be returned. As a result, this is probably useful only for maps.
+Retrieves a resource object by URL of the resource, unless the optional
+multiple parameter is included in wahich caes an array of resource
+objects is returned. If passed a resource object, it will simply return
+it, so it is safe to use this method in code like
+"$res = $navmap->getResourceByUrl($res)"
+if you're not sure if $res is already an object, or just a URL. If the
+resource appears multiple times in the course, only the first instance
+will be returned (useful for maps), unless the multiple parameter has
+been included, in which case all instances are returned in an array.
=item * B(map, filterFunc, recursive, bailout, showall):
@@ -2557,31 +2614,54 @@ want to know is if I resources matc
parameter will allow you to avoid potentially expensive enumeration of
all matching resources.
-=item * B(map, filterFunc, recursive):
+=item * B(map, filterFunc, recursive, showall):
Convience method for
- scalar(retrieveResources($map, $filterFunc, $recursive, 1)) > 0
+ scalar(retrieveResources($map, $filterFunc, $recursive, 1, $showall)) > 0
which will tell whether the map has resources matching the description
in the filter function.
+=item * B(url):
+
+Retrieves version infomation for a url. Returns the version (a number, or
+the string "mostrecent") for resources which have version information in
+the big hash.
+
=cut
sub getResourceByUrl {
my $self = shift;
my $resUrl = shift;
+ my $multiple = shift;
if (ref($resUrl)) { return $resUrl; }
$resUrl = &Apache::lonnet::clutter($resUrl);
- my $resId = $self->{NAV_HASH}->{'ids_' . $resUrl};
- if ($resId =~ /,/) {
- $resId = (split (/,/, $resId))[0];
+ if (defined($multiple)) {
+ if ($multiple) {
+ my @resIds = $self->{NAV_HASH}->{'ids_' . $resUrl};
+ }
}
+ my $resId = $self->{NAV_HASH}->{'ids_' . $resUrl};
if (!$resId) { return ''; }
- return $self->getById($resId);
+ if ($multiple) {
+ my @resources = ();
+ my @resIds = split (/,/, $resId);
+ foreach my $id (@resIds) {
+ if ($id) {
+ push(@resources,$self->getById($id));
+ }
+ }
+ return @resources;
+ } else {
+ if ($resId =~ /,/) {
+ $resId = (split (/,/, $resId))[0];
+ }
+ return $self->getById($resId);
+ }
}
sub retrieveResources {
@@ -2644,14 +2724,23 @@ sub hasResource {
my $map = shift;
my $filterFunc = shift;
my $recursive = shift;
+ my $showall = shift;
- return scalar($self->retrieveResources($map, $filterFunc, $recursive, 1)) > 0;
+ return scalar($self->retrieveResources($map, $filterFunc, $recursive, 1, $showall)) > 0;
+}
+
+sub usedVersion {
+ my $self = shift;
+ my $linkurl = shift;
+ return $self->navhash("version_$linkurl");
}
1;
package Apache::lonnavmaps::iterator;
use WeakRef;
+use Apache::lonnet;
+
=pod
=back
@@ -3131,6 +3220,8 @@ sub populateStack {
package Apache::lonnavmaps::DFSiterator;
use WeakRef;
+use Apache::lonnet;
+
# Not documented in the perldoc: This is a simple iterator that just walks
# through the nav map and presents the resources in a depth-first search
# fashion, ignorant of conditionals, randomized resources, etc. It presents
@@ -3508,6 +3599,15 @@ sub shown_symb {
if ($self->encrypted()) {return &Apache::lonenc::encrypted($self->symb());}
return $self->symb();
}
+sub id {
+ my $self=shift;
+ return $self->{ID};
+}
+sub enclosing_map_src {
+ my $self=shift;
+ (my $first, my $second) = $self->{ID} =~ /(\d+).(\d+)/;
+ return $self->navHash('map_id_'.$first);
+}
sub symb {
my $self=shift;
(my $first, my $second) = $self->{ID} =~ /(\d+).(\d+)/;
@@ -3516,12 +3616,16 @@ sub symb {
. '___' . $second . '___' . $symbSrc;
return &Apache::lonnet::symbclean($symb);
}
+sub wrap_symb {
+ my $self = shift;
+ return $self->{NAV_MAP}->wrap_symb($self->symb());
+}
sub title {
my $self=shift;
if ($self->{ID} eq '0.0') {
# If this is the top-level map, return the title of the course
# since this map can not be titled otherwise.
- return $ENV{'course.'.$ENV{'request.course.id'}.'.description'};
+ return $env{'course.'.$env{'request.course.id'}.'.description'};
}
return $self->navHash("title_", 1); }
# considered private and undocumented
@@ -3535,7 +3639,19 @@ sub condition {
my $condition=&Apache::lonnet::directcondval($condid);
return $condition;
}
-
+sub condval {
+ my $self=shift;
+ my $uri=&Apache::lonnet::deversion(&Apache::lonnet::declutter($self->src()));
+ my ($pathname,$filename)=($uri=~m|(.*)/([^/]*)|);
+ $pathname=~s/^adm\/wrapper\///;
+
+ my $match=($env{'acc.res.'.$env{'request.course.id'}.'.'.$pathname}=~
+ /\&\Q$filename\E\:([\d\|]+)\&/);
+ if ($match) {
+ return &Apache::lonnet::condval($1);
+ }
+ return 0;
+}
sub compTitle {
my $self = shift;
my $title = $self->title();
@@ -3601,7 +3717,7 @@ sub is_page {
sub is_problem {
my $self=shift;
my $src = $self->src();
- return ($src =~ /\.(problem|exam|quiz|assess|survey|form|library)$/)
+ return ($src =~ /\.(problem|exam|quiz|assess|survey|form|library|task)$/)
}
sub contains_problem {
my $self=shift;
@@ -3817,6 +3933,10 @@ sub duedate {
}
return $self->parmval("duedate", $part);
}
+sub handgrade {
+ (my $self, my $part) = @_;
+ return $self->parmval("handgrade", $part);
+}
sub maxtries {
(my $self, my $part) = @_;
return $self->parmval("maxtries", $part);
@@ -3855,9 +3975,9 @@ sub weight {
my $self = shift; my $part = shift;
if (!defined($part)) { $part = '0'; }
return &Apache::lonnet::EXT('resource.'.$part.'.weight',
- $self->symb(), $ENV{'user.domain'},
- $ENV{'user.name'},
- $ENV{'request.course.sec'});
+ $self->symb(), $env{'user.domain'},
+ $env{'user.name'},
+ $env{'request.course.sec'});
}
sub part_display {
my $self= shift(); my $partID = shift();
@@ -4101,7 +4221,7 @@ sub extractParts {
return;
}
foreach (split(/\,/,$metadata)) {
- if ($_ =~ /^part_(.*)$/) {
+ if ($_ =~ /^(?:part|Task)_(.*)$/) {
my $part = $1;
# This floods the logs if it blows up
if (defined($parts{$part})) {
@@ -4366,14 +4486,17 @@ sub ATTEMPTED { return 16; }
sub getCompletionStatus {
my $self = shift;
+ my $part = shift;
return $self->NETWORK_FAILURE if ($self->{NAV_MAP}->{NETWORK_FAILURE});
- my $status = $self->queryRestoreHash('solved', shift);
+ my $status = $self->queryRestoreHash('solved', $part);
# Left as separate if statements in case we ever do more with this
if ($status eq 'correct_by_student') {return $self->CORRECT;}
if ($status eq 'correct_by_scantron') {return $self->CORRECT;}
- if ($status eq 'correct_by_override') {return $self->CORRECT_BY_OVERRIDE; }
+ if ($status eq 'correct_by_override') {
+ return $self->CORRECT_BY_OVERRIDE;
+ }
if ($status eq 'incorrect_attempted') {return $self->INCORRECT; }
if ($status eq 'incorrect_by_override') {return $self->INCORRECT_BY_OVERRIDE; }
if ($status eq 'excused') {return $self->EXCUSED; }
@@ -4477,6 +4600,7 @@ An answer has been submitted, but the st
sub TRIES_LEFT { return 20; }
sub ANSWER_SUBMITTED { return 21; }
+sub PARTIALLY_CORRECT{ return 22; }
sub status {
my $self = shift;
@@ -4495,14 +4619,29 @@ sub status {
my $suppressFeedback = $self->problemstatus($part) eq 'no';
# If there's an answer date and we're past it, don't
# suppress the feedback; student should know
- if ($self->answerdate($part) && $self->answerdate($part) < time()) {
+ if ($self->duedate($part) && $self->duedate($part) < time() &&
+ $self->answerdate($part) && $self->answerdate($part) < time()) {
$suppressFeedback = 0;
}
# There are a few whole rows we can dispose of:
if ($completionStatus == CORRECT ||
$completionStatus == CORRECT_BY_OVERRIDE ) {
- return $suppressFeedback? ANSWER_SUBMITTED : CORRECT;
+ if ( $suppressFeedback ) { return ANSWER_SUBMITTED }
+ my $awarded=$self->awarded($part);
+ if ($awarded < 1 && $awarded > 0) {
+ return PARTIALLY_CORRECT;
+ } elsif ($awarded<1) {
+ return INCORRECT;
+ }
+ return CORRECT;
+ }
+
+ # If it's WRONG... and not open
+ if ( ($completionStatus == INCORRECT ||
+ $completionStatus == INCORRECT_BY_OVERRIDE)
+ && (!$self->opendate($part) || $self->opendate($part) > time()) ) {
+ return INCORRECT;
}
if ($completionStatus == ATTEMPTED) {
@@ -4589,6 +4728,7 @@ my %compositeToSimple =
NETWORK_FAILURE() => ERROR,
NOTHING_SET() => CLOSED,
CORRECT() => CORRECT,
+ PARTIALLY_CORRECT() => PARTIALLY_CORRECT,
EXCUSED() => CORRECT,
PAST_DUE_NO_ANSWER() => INCORRECT,
PAST_DUE_ANSWER_LATER() => INCORRECT,
@@ -4709,7 +4849,7 @@ sub getNext {
my $to = $self->to();
foreach my $branch ( split(/,/, $to) ) {
my $choice = $self->{NAV_MAP}->getById($branch);
- if (!$choice->condition()) { next; }
+ #if (!$choice->condition()) { next; }
my $next = $choice->goesto();
$next = $self->{NAV_MAP}->getById($next);