--- loncom/interface/lonnavmaps.pm 2004/08/13 20:12:11 1.275
+++ loncom/interface/lonnavmaps.pm 2004/12/07 22:58:05 1.307
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Navigate Maps Handler
#
-# $Id: lonnavmaps.pm,v 1.275 2004/08/13 20:12:11 albertel Exp $
+# $Id: lonnavmaps.pm,v 1.307 2004/12/07 22:58:05 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -33,9 +33,11 @@ use strict;
use Apache::Constants qw(:common :http);
use Apache::loncommon();
use Apache::lonmenu();
+use Apache::lonenc();
use Apache::lonlocal;
+use Apache::lonnet;
use POSIX qw (floor strftime);
-use Data::Dumper; # for debugging, not always used
+use Data::Dumper; # for debugging, not always
# symbolic constants
sub SYMB { return 1; }
@@ -84,7 +86,7 @@ my %colormap =
my $hurryUpColor = "#FF0000";
sub launch_win {
- my ($mode,$script)=@_;
+ my ($mode,$script,$toplinkitems)=@_;
my $result;
if ($script ne 'no') {
$result.='';
}
if ($mode eq 'link') {
- $result.=''
- .&mt("Launch navigation in separate window")." ";
+ &add_linkitem($toplinkitems,'launchnav','launch_navmapwin()',
+ "Launch navigation window");
}
return $result;
}
@@ -161,6 +163,8 @@ sub real_handler {
&Apache::loncommon::no_cache($r);
$r->send_http_header;
+ my %toplinkitems=();
+
if ($ENV{QUERY_STRING} eq 'collapseExternal') {
&Apache::lonnet::put('environment',{'remotenavmap' => 'off'});
&Apache::lonnet::appenv('environment.remotenavmap' => 'off');
@@ -172,6 +176,13 @@ sub real_handler {
swmenu.clearTimeout(swmenu.menucltim);
$navstatus
MENU
+ } else {
+ my $nothing = &Apache::lonhtmlcommon::javascript_nothing();
+ my $mainwindow='window.open('.$nothing.',"loncapaclient","",false);';
+ $menu=(<
");
# renderer call
- my $renderArgs = { 'cols' => [0,2,3],
+ my $renderArgs = { 'cols' => [0,1,2,3],
'sort' => $ENV{'form.sort'},
'url' => '/adm/navmaps',
'navmap' => $navmap,
@@ -370,9 +390,11 @@ ENDSUBM
'suppressEmptySequences' => $suppressEmptySequences,
'filterFunc' => $filterFunc,
'resource_no_folder_link' => $resource_no_folder_link,
- 'r' => $r};
+ 'sort_html'=> $sort_html,
+ 'r' => $r,
+ 'caller' => 'navmapsdisplay',
+ 'linkitems' => \%toplinkitems};
my $render = render($renderArgs);
- $navmap->untieHashes();
# If no resources were printed, print a reassuring message so the
# user knows there was no error.
@@ -422,7 +444,7 @@ sub getLinkForResource {
foreach $res (@$stack) {
if (defined($res)) {
if ($res->is_page()) {
- return $res->src();
+ return $res->link();
}
# in case folder was skipped over as "only sequence"
my ($map,$id,$src)=&Apache::lonnet::decode_symb($res->symb());
@@ -441,7 +463,7 @@ sub getLinkForResource {
if (defined($_)) { $res = $_; }
}
- return $res->src();
+ return $res->link();
}
# Convenience function: This separates the logic of how to create
@@ -642,7 +664,7 @@ sub timeToHumanString {
}
# Not this year, so show the year
- my $timeStr = strftime("on %A, %b %e %G at %I:%M %P", localtime($time));
+ my $timeStr = strftime("on %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;
@@ -989,6 +1011,15 @@ sub render_resource {
my $nonLinkedText = ''; # stuff after resource title not in link
my $link = $params->{"resourceLink"};
+
+ # The URL part is not escaped at this point, but the symb is...
+ # The stuff to the left of the ? must have ' replaced by \' since
+ # it will be quoted with ' in the href.
+
+ my ($left,$right) = split(/\?/, $link);
+ $left =~ s/'/\\'/g;
+ $link = $left.'?'.$right;
+
my $src = $resource->src();
my $it = $params->{"iterator"};
my $filter = $it->{FILTER};
@@ -997,28 +1028,31 @@ sub render_resource {
my $partLabel = "";
my $newBranchText = "";
-
+ 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 $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
@@ -1033,7 +1067,7 @@ sub render_resource {
if (!$params->{'resource_no_folder_link'}) {
$icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . '.gif';
- $icon = "";
+ $icon = "";
$linkopen = "{'queryString'} . '&filter=';
@@ -1046,11 +1080,12 @@ sub render_resource {
'&jump=' .
&Apache::lonnet::escape($resource->symb()) .
"&folderManip=1'>";
+
} else {
# Don't allow users to manipulate folder
$icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') .
'.nomanip.gif';
- $icon = "";
+ $icon = "";
$linkopen = "";
$linkclose = "";
@@ -1073,6 +1108,7 @@ sub render_resource {
}
# Decide what to display
+
$result .= "$newBranchText$linkopen$icon$linkclose";
my $curMarkerBegin = '';
@@ -1118,10 +1154,10 @@ sub render_communication_status {
my $link = $params->{"resourceLink"};
my $linkopen = "";
my $linkclose = "";
-
+ my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc");
if ($resource->hasDiscussion()) {
$discussionHTML = $linkopen .
- '' .
+ '' .
$linkclose;
}
@@ -1131,7 +1167,7 @@ sub render_communication_status {
if ($_) {
$feedbackHTML .= ' '
- . '';
}
}
@@ -1146,7 +1182,7 @@ sub render_communication_status {
$errorcount++;
$errorHTML .= ' '
- . '';
}
}
@@ -1175,7 +1211,9 @@ sub render_quick_status {
my $icon = $statusIconMap{$resource->simpleStatus($part)};
my $alt = $iconAltTags{$icon};
if ($icon) {
- $result .= "
$linkopen$linkclose
\n";
+ my $location=
+ &Apache::loncommon::lonhttpdurl("/adm/lonIcons/$icon");
+ $result .= "
$linkopen$linkclose
\n";
} else {
$result .= "
\n";
}
@@ -1306,11 +1344,17 @@ sub setDefault {
return $val;
}
+sub cmp_title {
+ my ($atitle,$btitle) = (lc($_[0]->compTitle),lc($_[1]->compTitle));
+ $atitle=~s/^\s*//;
+ $btitle=~s/^\s*//;
+ return $atitle cmp $btitle;
+}
+
sub render {
my $args = shift;
&Apache::loncommon::get_unprocessed_cgi($ENV{QUERY_STRING});
my $result = '';
-
# Configure the renderer.
my $cols = $args->{'cols'};
if (!defined($cols)) {
@@ -1373,7 +1417,7 @@ sub render {
# Determine where the "here" marker is and where the screen jumps to.
if ($ENV{'form.postsymb'}) {
- $here = $jump = $ENV{'form.postsymb'};
+ $here = $jump = &Apache::lonnet::symbclean($ENV{'form.postsymb'});
} elsif ($ENV{'form.postdata'}) {
# couldn't find a symb, is there a URL?
my $currenturl = $ENV{'form.postdata'};
@@ -1381,10 +1425,7 @@ sub render {
#$currenturl=~s/^[^\/]+//;
$here = $jump = &Apache::lonnet::symbread($currenturl);
- } else {
- &Apache::lonnet::logthis("Hrrm,");
-
- }
+ }
# Step three: Ensure the folders are open
my $mapIterator = $navmap->getIterator(undef, undef, undef, 1);
@@ -1394,7 +1435,7 @@ sub render {
# We only need to do this if we need to open the maps to show the
# current position. This will change the counter so we can't count
# for the jump marker with this loop.
- while (($curRes = $mapIterator->next()) && !$found) {
+ while ($here && ($curRes = $mapIterator->next()) && !$found) {
if (ref($curRes) && $curRes->symb() eq $here) {
my $mapStack = $mapIterator->getStack();
@@ -1470,23 +1511,24 @@ sub render {
my $printKey = $args->{'printKey'};
my $printCloseAll = $args->{'printCloseAll'};
if (!defined($printCloseAll)) { $printCloseAll = 1; }
-
+
# Print key?
if ($printKey) {
$result .= '
';
my $date=localtime;
$result.='
Key:
';
+ my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc");
if ($navmap->{LAST_CHECK}) {
$result .=
- ' '.&mt('New discussion since').' '.
+ ' '.&mt('New discussion since').' '.
strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})).
'
';
+ if ($ENV{'environment.remotenavmap'} ne 'on') {
+ $result .= '
';
} else {
- $result .= ' ';
+ $result .= '
';
+ }
+ $result.=&show_linkitems($args->{'linkitems'});
+ if ($args->{'sort_html'}) {
+ if ($ENV{'environment.remotenavmap'} ne 'on') {
+ $result.='
'.
+ '
'.$args->{'sort_html'}.'
';
+ } else {
+ $result.='
'.
+ $args->{'sort_html'}.'
';
+ }
}
+ $result .= '
';
+ } elsif ($args->{'sort_html'}) {
+ $result.=$args->{'sort_html'};
}
+
$result .= " \n";
if ($r) {
$r->print($result);
@@ -1561,7 +1638,9 @@ END
$args->{'indentLevel'} = 0;
$args->{'isNewBranch'} = 0;
$args->{'condensed'} = 0;
- $args->{'indentString'} = setDefault($args->{'indentString'}, "");
+ my $location=
+ &Apache::loncommon::lonhttpdurl("/adm/lonIcons/whitespace1.gif");
+ $args->{'indentString'} = setDefault($args->{'indentString'}, "");
$args->{'displayedHereMarker'} = 0;
# If we're suppressing empty sequences, look for them here. Use DFS for speed,
@@ -1614,22 +1693,49 @@ END
my @resources;
my $code='';# sub { !(shift->is_map();) };
if ($args->{'sort'} eq 'title') {
- @resources=$navmap->retrieveResources(undef,
- sub { !shift->is_map(); });
- @resources= sort {lc($a->compTitle) cmp lc($b->compTitle)} @resources;
+ my $oldFilterFunc = $filterFunc;
+ my $filterFunc=
+ sub {
+ my ($res)=@_;
+ if ($res->is_map()) { return 0;}
+ return &$oldFilterFunc($res);
+ };
+ @resources=$navmap->retrieveResources(undef,$filterFunc);
+ @resources= sort { &cmp_title($a,$b) } @resources;
} elsif ($args->{'sort'} eq 'duedate') {
- @resources=$navmap->retrieveResources(undef,
- sub { shift->is_problem(); });
- @resources= sort
- {
+ my $oldFilterFunc = $filterFunc;
+ my $filterFunc=
+ sub {
+ my ($res)=@_;
+ if (!$res->is_problem()) { return 0;}
+ return &$oldFilterFunc($res);
+ };
+ @resources=$navmap->retrieveResources(undef,$filterFunc);
+ @resources= sort {
if ($a->duedate ne $b->duedate) {
return $a->duedate cmp $b->duedate;
- } else {
- lc($a->compTitle) cmp lc($b->compTitle)
}
+ my $value=&cmp_title($a,$b);
+ return $value;
} @resources;
+ } elsif ($args->{'sort'} eq 'discussion') {
+ my $oldFilterFunc = $filterFunc;
+ my $filterFunc=
+ sub {
+ my ($res)=@_;
+ if (!$res->hasDiscussion() &&
+ !$res->getFeedback() &&
+ !$res->getErrors()) { return 0;}
+ return &$oldFilterFunc($res);
+ };
+ @resources=$navmap->retrieveResources(undef,$filterFunc);
+ @resources= sort { &cmp_title($a,$b) } @resources;
+ } else {
+ #unknow sort mechanism or default
+ undef($args->{'sort'});
}
+
while (1) {
if ($args->{'sort'}) {
$curRes = shift(@resources);
@@ -1765,7 +1871,7 @@ END
my $srcHasQuestion = $src =~ /\?/;
$args->{"resourceLink"} = $src.
($srcHasQuestion?'&':'?') .
- 'symb=' . &Apache::lonnet::escape($curRes->symb()).
+ 'symb=' . &Apache::lonnet::escape($curRes->shown_symb()).
$anchor;
# Now, display each column.
@@ -1833,8 +1939,46 @@ if (location.href.indexOf('#curloc')==-1
$r->rflush();
}
- if ($mustCloseNavMap) { $navmap->untieHashes(); }
+ return $result;
+}
+
+sub add_linkitem {
+ my ($linkitems,$name,$cmd,$text)=@_;
+ $$linkitems{$name}{'cmd'}=$cmd;
+ $$linkitems{$name}{'text'}=&mt($text);
+}
+sub show_linkitems {
+ my ($linkitems)=@_;
+ my @linkorder = ("launchnav","closenav","firsthomework","everything",
+ "uncompleted","changefolder","clearbubbles");
+
+ my $result .= (<
+
+ '."\n";
+
return $result;
}
@@ -1895,10 +2039,6 @@ successful, or B if not.
=back
-When you are done with the $navmap object, you I call
-$navmap->untieHashes(), or you'll prevent the current user from using that
-course until the web server is restarted. (!)
-
=head2 Methods
=over 4
@@ -2044,17 +2184,11 @@ sub generate_email_discuss_status {
foreach my $msgid (split(/\&/, $keys)) {
$msgid=&Apache::lonnet::unescape($msgid);
- my $plain=&Apache::lonnet::unescape(&Apache::lonnet::unescape($msgid));
- if ($plain=~/(Error|Feedback) \[([^\]]+)\]/) {
- my ($what,$url)=($1,$2);
- my %status=
- &Apache::lonnet::get('email_status',[$msgid]);
- if ($status{$msgid}=~/^error\:/) {
- $status{$msgid}='';
- }
-
- if (($status{$msgid} eq 'new') ||
- (!$status{$msgid})) {
+ 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') {
$error{$url}.=','.$msgid;
} else {
@@ -2064,8 +2198,10 @@ sub generate_email_discuss_status {
}
}
+ #url's of resources that have feedbacks
$self->{FEEDBACK} = \%feedback;
- $self->{ERROR_MSG} = \%error; # what is this? JB
+ #or errors
+ $self->{ERROR_MSG} = \%error;
$self->{DISCUSSION_TIME} = \%discussiontime;
$self->{EMAIL_STATUS} = \%emailstatus;
$self->{LAST_READ} = \%lastreadtime;
@@ -2119,13 +2255,6 @@ sub getIterator {
return $iterator;
}
-# unties the hash when done
-sub untieHashes {
- my $self = shift;
- untie %{$self->{NAV_HASH}};
- untie %{$self->{PARM_HASH}};
-}
-
# Private method: Does the given resource (as a symb string) have
# current discussion? Returns 0 if chat/mail data not extracted.
sub hasDiscussion {
@@ -2222,9 +2351,14 @@ sub getById {
sub getBySymb {
my $self = shift;
my $symb = shift;
+
my ($mapUrl, $id, $filename) = &Apache::lonnet::decode_symb($symb);
my $map = $self->getResourceByUrl($mapUrl);
- return $self->getById($map->map_pc() . '.' . $id);
+ my $returnvalue = undef;
+ if (ref($map)) {
+ $returnvalue = $self->getById($map->map_pc() .'.'.$id);
+ }
+ return $returnvalue;
}
sub getByMapPc {
@@ -2418,6 +2552,7 @@ in the filter function.
=cut
+
sub getResourceByUrl {
my $self = shift;
my $resUrl = shift;
@@ -2500,7 +2635,7 @@ sub hasResource {
1;
package Apache::lonnavmaps::iterator;
-
+use WeakRef;
=pod
=back
@@ -2640,7 +2775,7 @@ sub new {
my $class = ref($proto) || $proto;
my $self = {};
- $self->{NAV_MAP} = shift;
+ weaken($self->{NAV_MAP} = shift);
return undef unless ($self->{NAV_MAP});
# Handle the parameters
@@ -2976,7 +3111,7 @@ sub populateStack {
1;
package Apache::lonnavmaps::DFSiterator;
-
+use WeakRef;
# 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
@@ -3004,7 +3139,7 @@ sub new {
my $class = ref($proto) || $proto;
my $self = {};
- $self->{NAV_MAP} = shift;
+ weaken($self->{NAV_MAP} = shift);
return undef unless ($self->{NAV_MAP});
$self->{FIRST_RESOURCE} = shift || $self->{NAV_MAP}->firstResource();
@@ -3158,7 +3293,7 @@ sub populateStack {
1;
package Apache::lonnavmaps::resource;
-
+use WeakRef;
use Apache::lonnet;
=pod
@@ -3240,7 +3375,7 @@ sub new {
my $class = ref($proto) || $proto;
my $self = {};
- $self->{NAV_MAP} = shift;
+ weaken($self->{NAV_MAP} = shift);
$self->{ID} = shift;
# Store this new resource in the parent nav map's cache.
@@ -3328,6 +3463,7 @@ Returns the title of the resource.
# These info functions can be used directly, as they don't return
# resource information.
sub comesfrom { my $self=shift; return $self->navHash("comesfrom_", 1); }
+sub encrypted { my $self=shift; return $self->navHash("encrypted_", 1); }
sub ext { my $self=shift; return $self->navHash("ext_", 1) eq 'true:'; }
sub from { my $self=shift; return $self->navHash("from_", 1); }
# considered private and undocumented
@@ -3339,10 +3475,20 @@ sub randompick {
return $self->{NAV_MAP}->{PARM_HASH}->{$self->symb .
'.0.parameter_randompick'};
}
+sub link {
+ my $self=shift;
+ if ($self->encrypted()) { return &Apache::lonenc::encrypted($self->src); }
+ return $self->src;
+}
sub src {
my $self=shift;
return $self->navHash("src_", 1);
}
+sub shown_symb {
+ my $self=shift;
+ if ($self->encrypted()) {return &Apache::lonenc::encrypted($self->symb());}
+ return $self->symb();
+}
sub symb {
my $self=shift;
(my $first, my $second) = $self->{ID} =~ /(\d+).(\d+)/;
@@ -3361,6 +3507,16 @@ sub title {
return $self->navHash("title_", 1); }
# considered private and undocumented
sub to { my $self=shift; return $self->navHash("to_", 1); }
+sub condition {
+ my $self=shift;
+ my $undercond=$self->navHash("undercond_", 1);
+ if (!defined($undercond)) { return 1; };
+ my $condid=$self->navHash("condid_$undercond");
+ if (!defined($condid)) { return 1; };
+ my $condition=&Apache::lonnet::directcondval($condid);
+ return $condition;
+}
+
sub compTitle {
my $self = shift;
my $title = $self->title();
@@ -3782,6 +3938,16 @@ Returns the number of parts of the probl
for single part problems, returns 1. For multipart, it returns the
number of parts in the problem, not including psuedo-part 0.
+=item * B():
+
+Returns the total number of responses in the problem a student can answer.
+
+=item * B():
+
+Returns a hash whose keys are the response types. The values are the number
+of times each response type is used. This is for the I problem, not
+just a single part.
+
=item * B():
Returns true if the problem is multipart, false otherwise. Use this instead
@@ -3828,6 +3994,26 @@ sub countParts {
return scalar(@{$parts}); # + $delta;
}
+sub countResponses {
+ my $self = shift;
+ my $count;
+ foreach my $part (@{$self->parts()}) {
+ $count+= scalar($self->responseIds($part));
+ }
+ return $count;
+}
+
+sub responseTypes {
+ my $self = shift;
+ my %responses;
+ foreach my $part ($self->parts()) {
+ foreach my $responsetype ($self->responseType($part)) {
+ $responses{$responsetype}++ if (defined($responsetype));
+ }
+ }
+ return %responses;
+}
+
sub multipart {
my $self = shift;
return $self->countParts() > 1;
@@ -3915,6 +4101,7 @@ sub extractParts {
}
+ # These hashes probably do not need names that end with "Hash"....
my %responseIdHash;
my %responseTypeHash;
@@ -3950,17 +4137,27 @@ sub extractParts {
}
}
my $resorder = &Apache::lonnet::metadata($self->src(),'responseorder');
+ #
+ # Reorder the arrays in the %responseIdHash and %responseTypeHash
if ($resorder) {
my @resorder=split(/,/,$resorder);
foreach my $part (keys(%responseIdHash)) {
- my %resids = map { ($_,1) } @{ $responseIdHash{$part} };
+ my $i=0;
+ my %resids = map { ($_,$i++) } @{ $responseIdHash{$part} };
my @neworder;
foreach my $possibleid (@resorder) {
if (exists($resids{$possibleid})) {
- push(@neworder,$possibleid);
+ push(@neworder,$resids{$possibleid});
}
}
- $responseIdHash{$part}=\@neworder;
+ my @ids;
+ my @type;
+ foreach my $element (@neworder) {
+ push (@ids,$responseIdHash{$part}->[$element]);
+ push (@type,$responseTypeHash{$part}->[$element]);
+ }
+ $responseIdHash{$part}=\@ids;
+ $responseTypeHash{$part}=\@type;
}
}
$self->{RESPONSE_IDS} = \%responseIdHash;
@@ -4156,6 +4353,7 @@ sub getCompletionStatus {
# 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 'incorrect_attempted') {return $self->INCORRECT; }
if ($status eq 'incorrect_by_override') {return $self->INCORRECT_BY_OVERRIDE; }
@@ -4492,6 +4690,7 @@ sub getNext {
my $to = $self->to();
foreach my $branch ( split(/,/, $to) ) {
my $choice = $self->{NAV_MAP}->getById($branch);
+ if (!$choice->condition()) { next; }
my $next = $choice->goesto();
$next = $self->{NAV_MAP}->getById($next);
500 Internal Server Error
Internal Server Error
The server encountered an internal error or
misconfiguration and was unable to complete
your request.
Please contact the server administrator at
root@localhost to inform them of the time this error occurred,
and the actions you performed just before this error.
More information about this error may be available
in the server error log.