--- loncom/interface/lonnavmaps.pm 2005/11/22 18:58:37 1.349.2.1
+++ loncom/interface/lonnavmaps.pm 2006/03/13 17:29:50 1.368
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Navigate Maps Handler
#
-# $Id: lonnavmaps.pm,v 1.349.2.1 2005/11/22 18:58:37 albertel Exp $
+# $Id: lonnavmaps.pm,v 1.368 2006/03/13 17:29:50 raeburn 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 => ''
@@ -530,7 +530,8 @@ sub getDescription {
if ($status == $res->PAST_DUE_NO_ANSWER) {
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) {
@@ -1094,7 +1095,7 @@ sub render_resource {
if ($resource->is_problem()) {
if ($part eq '0' || $params->{'condensed'}) {
- $icon ='';
+ $icon ='';
} else {
$icon = $params->{'indentString'};
}
@@ -1111,11 +1112,12 @@ 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 = "";
+ $icon = "";
$linkopen = "{'url'} . '?' .
$params->{'queryString'} . '&filter=';
@@ -1133,8 +1135,8 @@ sub render_resource {
# Don't allow users to manipulate folder
$icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') .
'.nomanip.gif';
- $icon = "";
+ $icon = "";
$linkopen = "";
$linkclose = "";
@@ -1142,10 +1144,10 @@ sub render_resource {
}
if ($resource->randomout()) {
- $nonLinkedText .= ' (hidden) ';
+ $nonLinkedText .= ' ('.&mt('hidden').') ';
}
if (!$resource->condval()) {
- $nonLinkedText .= ' (conditionally hidden) ';
+ $nonLinkedText .= ' ('.&mt('conditionally hidden').') ';
}
# We're done preparing and finally ready to start the rendering
@@ -1177,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: [_1]', $displaypart).")";
if ($link!~/\#/) { $link.='#'.&Apache::lonnet::escape($part); }
$title = "";
}
if ($params->{'condensed'} && $resource->countParts() > 1) {
- $nonLinkedText .= ' (' . $resource->countParts() . ' parts)';
+ $nonLinkedText .= ' ('.&mt('[_1] parts', $resource->countParts()).')';
}
my $target;
@@ -1471,8 +1473,8 @@ sub render {
$navmap = Apache::lonnavmaps::navmap->new();
if (!defined($navmap)) {
# no londer in course
- return 'No course selected
- Select a course
';
+ return ''.&mt('No course selected').'
+ '.&mt('Select a course').'
';
}
}
@@ -2065,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
@@ -2265,6 +2268,27 @@ 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};
+ }
+
+ $self->generate_email_discuss_status();
+
+ 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::dumpstore($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 {
@@ -2320,6 +2344,61 @@ sub hasDiscussion {
}
}
+sub last_post_time {
+ my $self = shift;
+ my $symb = shift;
+ my $ressymb = $self->wrap_symb($symb);
+ return $self->{DISCUSSION_TIME}->{$ressymb};
+}
+
+sub unread_discussion {
+ my $self = shift;
+ my $symb = shift;
+
+ $self->get_discussion_data();
+
+ my $ressymb = $self->wrap_symb($symb);
+
+ my $version = $self->{DISCUSSION_DATA}{'version:'.$ressymb};
+ if (!$version) { return; }
+
+ my $prevread = $self->{LAST_READ}{$ressymb};
+
+ my $unreadcount = 0;
+ my $hiddenflag = 0;
+ my $deletedflag = 0;
+ my ($hidden,$deleted);
+
+ my %subjects;
+
+ for (my $id=$version; $id>0; $id--) {
+ my $vkeys=$self->{DISCUSSION_DATA}{$id.':keys:'.$ressymb};
+ my @keys=split(/:/,$vkeys);
+ if (grep(/^hidden$/ ,@keys)) {
+ if (!$hiddenflag) {
+ $hidden = $self->{DISCUSSION_DATA}{$id.':'.$ressymb.':hidden'};
+ $hiddenflag = 1;
+ }
+ } elsif (grep(/^deleted$/,@keys)) {
+ if (!$deletedflag) {
+ $deleted = $self->{DISCUSSION_DATA}{$id.':'.$ressymb.':deleted'};
+ $deletedflag = 1;
+ }
+ } else {
+ if (($hidden !~/\.$id\./) && ($deleted !~/\.$id\./)
+ && $prevread < $self->{DISCUSSION_DATA}{$id.':'.$ressymb.':timestamp'}) {
+ $unreadcount++;
+ $subjects{$unreadcount}=
+ $id.': '.$self->{DISCUSSION_DATA}{$id.':'.$ressymb.':subject'};
+ }
+ }
+ }
+ if (wantarray) {
+ return ($unreadcount,\%subjects);
+ }
+ return $unreadcount
+}
+
sub wrap_symb {
my $self = shift;
my $symb = shift;
@@ -2475,6 +2554,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'};
@@ -2492,6 +2577,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;
@@ -2512,6 +2601,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}; }
@@ -2562,14 +2657,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):
@@ -2604,22 +2702,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 {
@@ -2687,10 +2804,16 @@ 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;
-use WeakRef;
+use Scalar::Util qw(weaken);
use Apache::lonnet;
=pod
@@ -3171,7 +3294,7 @@ sub populateStack {
1;
package Apache::lonnavmaps::DFSiterator;
-use WeakRef;
+use Scalar::Util qw(weaken);
use Apache::lonnet;
# Not documented in the perldoc: This is a simple iterator that just walks
@@ -3355,7 +3478,7 @@ sub populateStack {
1;
package Apache::lonnavmaps::resource;
-use WeakRef;
+use Scalar::Util qw(weaken);
use Apache::lonnet;
=pod
@@ -3534,8 +3657,7 @@ sub kind { my $self=shift; return $self-
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'};
+ return $self->{NAV_MAP}->{PARM_HASH}->{$self->symb().'.0.randompick'};
}
sub link {
my $self=shift;
@@ -3593,9 +3715,8 @@ sub 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 ($pathname,$filename) =
+ &Apache::lonnet::split_uri_for_cond($self->src());
my $match=($env{'acc.res.'.$env{'request.course.id'}.'.'.$pathname}=~
/\&\Q$filename\E\:([\d\|]+)\&/);
@@ -3666,10 +3787,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|task)$/)
+ if ($src =~ /\.(problem|exam|quiz|assess|survey|form|library|task)$/) {
+ return !($self->is_practice());
+ }
+ return 0;
}
sub contains_problem {
my $self=shift;
@@ -3696,6 +3828,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;
@@ -3980,6 +4117,19 @@ Returns a false value if there has been
logged in, true if there has. Always returns false if the discussion
data was not extracted when the nav map was constructed.
+=item * B:
+
+Returns a false value if there hasn't been discussion otherwise returns
+unix timestamp of last time a discussion posting (or edit) was made.
+
+=item * B:
+
+returns in scalar context the count of the number of unread discussion
+postings
+
+returns in list context both the count of postings and a hash ref
+containing the subjects of all unread postings
+
=item * B:
Gets the feedback for the resource and returns the raw feedback string
@@ -4000,6 +4150,16 @@ sub hasDiscussion {
return $self->{NAV_MAP}->hasDiscussion($self->symb());
}
+sub last_post_time {
+ my $self = shift;
+ return $self->{NAV_MAP}->last_post_time($self->symb());
+}
+
+sub unread_discussion {
+ my $self = shift;
+ return $self->{NAV_MAP}->unread_discussion($self->symb());
+}
+
sub getFeedback {
my $self = shift;
my $source = $self->src();
@@ -4097,7 +4257,7 @@ sub countResponses {
sub responseTypes {
my $self = shift;
my %responses;
- foreach my $part ($self->parts()) {
+ foreach my $part (@{$self->parts()}) {
foreach my $responsetype ($self->responseType($part)) {
$responses{$responsetype}++ if (defined($responsetype));
}
@@ -4209,7 +4369,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 = '';