--- loncom/interface/lonnavmaps.pm 2005/06/28 21:41:43 1.332
+++ loncom/interface/lonnavmaps.pm 2006/02/24 22:35:12 1.362
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Navigate Maps Handler
#
-# $Id: lonnavmaps.pm,v 1.332 2005/06/28 21:41:43 albertel Exp $
+# $Id: lonnavmaps.pm,v 1.362 2006/02/24 22:35:12 www Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -57,7 +57,7 @@ my %statusIconMap =
$resObj->CLOSED => '',
$resObj->OPEN => 'navmap.open.gif',
$resObj->CORRECT => 'navmap.correct.gif',
- $resObj->PARTIALLY_CORRECT => 'navmap.ellipsis.gif',
+ $resObj->PARTIALLY_CORRECT => 'navmap.partial.gif',
$resObj->INCORRECT => 'navmap.wrong.gif',
$resObj->ATTEMPTED => 'navmap.ellipsis.gif',
$resObj->ERROR => ''
@@ -90,14 +90,20 @@ my %colormap =
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
@@ -218,6 +238,7 @@ ENDSUBM
$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')."");
@@ -290,7 +311,7 @@ ENDSUBM
if ($ENV{QUERY_STRING} eq 'launchExternal') {
$r->print('
- ');
$r->print('
@@ -494,22 +515,23 @@ 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 || $status == $res->PARTIALLY_CORRECT) {
+ if (($status == $res->ANSWER_OPEN || $status == $res->PARTIALLY_CORRECT)
+ && $res->handgrade($part) ne 'yes') {
return &mt("Answer available");
}
if ($status == $res->EXCUSED) {
@@ -529,7 +551,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";
@@ -582,8 +604,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');
@@ -654,30 +679,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();
}
}
@@ -1027,7 +1066,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();
@@ -1047,7 +1085,7 @@ sub render_resource {
# links to open and close the folder
- my $linkopen = "";
+ my $linkopen = "";
my $linkclose = "";
@@ -1057,7 +1095,7 @@ sub render_resource {
if ($resource->is_problem()) {
if ($part eq '0' || $params->{'condensed'}) {
- $icon ='
';
+ $icon ='
';
} else {
$icon = $params->{'indentString'};
}
@@ -1074,13 +1112,14 @@ sub render_resource {
}
my $folderType = $resource->is_sequence() ? 'folder' : 'page';
-
+ my $title=$resource->title;
+ $title=~s/\'/ /g;
if (!$params->{'resource_no_folder_link'}) {
$icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . '.gif';
$icon = "
";
+ ($nowOpen ? &mt('Open Folder') : &mt('Close Folder')).' '.$title."' border='0' />";
- $linkopen = "{'url'} . '?' .
$params->{'queryString'} . '&filter=';
$linkopen .= ($nowOpen xor $it->{CONDITION}) ?
addToFilter($filter, $mapId) :
@@ -1090,14 +1129,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 = "
";
+ ($nowOpen ? &mt('Open Folder') : &mt('Close Folder')).' '.$title."' border='0' />";
$linkopen = "";
$linkclose = "";
@@ -1105,7 +1144,10 @@ sub render_resource {
}
if ($resource->randomout()) {
- $nonLinkedText .= ' (hidden) ';
+ $nonLinkedText .= ' ('.&mt('hidden').') ';
+ }
+ if (!$resource->condval()) {
+ $nonLinkedText .= ' ('.&mt('conditionally hidden').') ';
}
# We're done preparing and finally ready to start the rendering
@@ -1129,7 +1171,7 @@ sub render_resource {
# Is this the current resource?
if (!$params->{'displayedHereMarker'} &&
$resource->symb() eq $params->{'here'} ) {
- $curMarkerBegin = '> ';
+ $curMarkerBegin = '>';
$curMarkerEnd = '<';
$params->{'displayedHereMarker'} = 1;
}
@@ -1137,13 +1179,13 @@ sub render_resource {
if ($resource->is_problem() && $part ne '0' &&
!$params->{'condensed'}) {
my $displaypart=$resource->part_display($part);
- $partLabel = " (Part: $displaypart)";
+ $partLabel = " (".&mt('Part').": $displaypart)";
if ($link!~/\#/) { $link.='#'.&Apache::lonnet::escape($part); }
$title = "";
}
if ($params->{'condensed'} && $resource->countParts() > 1) {
- $nonLinkedText .= ' (' . $resource->countParts() . ' parts)';
+ $nonLinkedText .= ' (' . $resource->countParts() . ' '.&mt('parts').')';
}
my $target;
@@ -1151,7 +1193,7 @@ sub render_resource {
$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";
}
@@ -1164,7 +1206,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()) {
@@ -1177,7 +1223,7 @@ sub render_communication_status {
my $feedback = $resource->getFeedback();
foreach (split(/\,/, $feedback)) {
if ($_) {
- $feedbackHTML .= ' '
. '
';
@@ -1192,7 +1238,7 @@ sub render_communication_status {
last if ($errorcount>=10); # Only output 10 bombs maximum
if ($_) {
$errorcount++;
- $errorHTML .= ' '
. '
';
@@ -1214,7 +1260,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() &&
@@ -1373,7 +1423,6 @@ sub render {
# no columns, no nav maps.
return '';
}
- my $mustCloseNavMap = 0;
my $navmap;
if (defined($args->{'navmap'})) {
$navmap = $args->{'navmap'};
@@ -1422,8 +1471,12 @@ sub render {
# 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 ''.&mt('No course selected').'
+ '.&mt('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.
@@ -1487,7 +1540,6 @@ sub render {
# 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
@@ -1808,10 +1860,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]);
@@ -2019,15 +2067,16 @@ In order of increasing complexity and po
=over 4
-=item * C<$navmap-EgetByX>, where X is B, B, B or B. This provides
+=item * C<$navmap-EgetByX>, where X is B, B or B and getResourceByUrl. This provides
various ways to obtain resource objects, based on various identifiers.
Use this when you want to request information about one object or
a handful of resources you already know the identities of, from some
other source. For more about Ids, Symbs, and MapPcs, see the
Resource documentation. Note that Url should be a B,
- not your first choice; it only works when there is only one
+ not your first choice; it only really works when there is only one
instance of the resource in the course, which only applies to
- maps, and even that may change in the future.
+ maps, and even that may change in the future (see the B
+ documentation for more details.)
=item * CretrieveResources(args)>. This
retrieves resources matching some criterion and returns them
@@ -2184,9 +2233,9 @@ sub generate_email_discuss_status {
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;
@@ -2219,6 +2268,25 @@ sub get_user_data {
$self->{RETRIEVED_USER_DATA} = 1;
}
+sub get_discussion_data {
+ my $self = shift;
+ if ($self->{RETRIEVED_DISCUSSION_DATA}) {
+ return $self->{DISCUSSION_DATA};
+ }
+
+ my $cid=$env{'request.course.id'};
+ my $cdom=$env{'course.'.$cid.'.domain'};
+ my $cnum=$env{'course.'.$cid.'.num'};
+
+ # Retrieve discussion data for resources in course
+ my %discussion_data = &Apache::lonnet::dump($cid,$cdom,$cnum);
+
+ $self->{DISCUSSION_DATA} = \%discussion_data;
+ $self->{RETRIEVED_DISCUSSION_DATA} = 1;
+ return $self->{DISCUSSION_DATA};
+}
+
+
# Internal function: Takes a key to look up in the nav hash and implements internal
# memory caching of that key.
sub navhash {
@@ -2429,6 +2497,12 @@ sub parmval_real {
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'};
@@ -2446,6 +2520,10 @@ sub parmval_real {
my $mapparm=$mapname.'___(all).'.$what;
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;
my $seclevelm=$usercourseprefix.'.['.$csec.'].'.$mapparm;
@@ -2466,6 +2544,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}; }
@@ -2516,14 +2600,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):
@@ -2558,22 +2645,41 @@ Convience method for
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 (!$resId) { return ''; }
- return $self->getById($resId);
+ if ($multiple) {
+ my @resources = ();
+ my @resIds = split (/,/, $resId);
+ foreach my $id (@resIds) {
+ my $resourceId = $self->getById($id);
+ if ($resourceId) {
+ push(@resources,$resourceId);
+ }
+ }
+ return @resources;
+ } else {
+ if ($resId =~ /,/) {
+ $resId = (split (/,/, $resId))[0];
+ }
+ return $self->getById($resId);
+ }
}
sub retrieveResources {
@@ -2641,6 +2747,12 @@ sub hasResource {
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;
@@ -3545,7 +3657,18 @@ sub condition {
my $condition=&Apache::lonnet::directcondval($condid);
return $condition;
}
+sub condval {
+ my $self=shift;
+ my ($pathname,$filename) =
+ &Apache::lonnet::split_uri_for_cond($self->src());
+ 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();
@@ -3608,10 +3731,21 @@ sub is_page {
return $self->navHash("is_map_", 1) &&
$self->navHash("map_type_" . $self->map_pc()) eq 'page';
}
+sub is_practice {
+ my $self=shift;
+ my ($part) = @_;
+ if ($self->parmval('type',$part) eq 'practice') {
+ return 1;
+ }
+ return 0;
+}
sub is_problem {
my $self=shift;
my $src = $self->src();
- return ($src =~ /\.(problem|exam|quiz|assess|survey|form|library)$/)
+ if ($src =~ /\.(problem|exam|quiz|assess|survey|form|library|task)$/) {
+ return !($self->is_practice());
+ }
+ return 0;
}
sub contains_problem {
my $self=shift;
@@ -3638,6 +3772,11 @@ sub is_survey {
}
return 0;
}
+sub is_task {
+ my $self=shift;
+ my $src = $self->src();
+ return ($src =~ /\.(task)$/)
+}
sub is_empty_sequence {
my $self=shift;
@@ -3827,6 +3966,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);
@@ -4111,7 +4254,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})) {
@@ -4147,7 +4290,8 @@ sub extractParts {
# where the part names begin and end, and even then, it is possible
# to construct ambiguous situations.
foreach (split /,/, $metadata) {
- if ($_ =~ /^([a-zA-Z]+)response_(.*)/) {
+ if ($_ =~ /^([a-zA-Z]+)response_(.*)/
+ || $_ =~ /^(Task)_(.*)/) {
my $responseType = $1;
my $partStuff = $2;
my $partIdSoFar = '';
@@ -4527,6 +4671,13 @@ sub status {
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) {
return ATTEMPTED;
}
@@ -4732,7 +4883,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);