--- loncom/interface/lonnavmaps.pm 2003/09/10 19:29:33 1.227 +++ loncom/interface/lonnavmaps.pm 2004/03/08 17:31:37 1.251 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.227 2003/09/10 19:29:33 bowersj2 Exp $ +# $Id: lonnavmaps.pm,v 1.251 2004/03/08 17:31:37 www Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,20 +25,7 @@ # # http://www.lon-capa.org/ # -# (Page Handler -# -# (TeX Content Handler -# -# 05/29/00,05/30 Gerd Kortemeyer) -# 08/30,08/31,09/06,09/14,09/15,09/16,09/19,09/20,09/21,09/23, -# 10/02,10/10,10/14,10/16,10/18,10/19,10/31,11/6,11/14,11/16 Gerd Kortemeyer) -# -# 3/1/1,6/1,17/1,29/1,30/1,2/8,9/21,9/24,9/25 Gerd Kortemeyer -# YEAR=2002 -# 1/1 Gerd Kortemeyer -# Oct-Nov Jeremy Bowers -# YEAR=2003 -# Jeremy Bowers ... lots of days +### package Apache::lonnavmaps; @@ -46,6 +33,7 @@ use strict; use Apache::Constants qw(:common :http); use Apache::loncommon(); use Apache::lonmenu(); +use Apache::lonlocal; use POSIX qw (floor strftime); use Data::Dumper; # for debugging, not always used @@ -106,9 +94,9 @@ sub real_handler { # Handle header-only request if ($r->header_only) { if ($ENV{'browser.mathml'}) { - $r->content_type('text/xml'); + &Apache::loncommon::content_type($r,'text/xml'); } else { - $r->content_type('text/html'); + &Apache::loncommon::content_type($r,'text/html'); } $r->send_http_header; return OK; @@ -116,9 +104,9 @@ sub real_handler { # Send header, don't cache this page if ($ENV{'browser.mathml'}) { - $r->content_type('text/xml'); + &Apache::loncommon::content_type($r,'text/xml'); } else { - $r->content_type('text/html'); + &Apache::loncommon::content_type($r,'text/html'); } &Apache::loncommon::no_cache($r); $r->send_http_header; @@ -126,7 +114,6 @@ sub real_handler { # Create the nav map my $navmap = Apache::lonnavmaps::navmap->new(); - if (!defined($navmap)) { my $requrl = $r->uri; $ENV{'user.error.msg'} = "$requrl:bre:0:0:Course not initialized"; @@ -134,7 +121,7 @@ sub real_handler { } $r->print("\n"); - $r->print("Navigate Course Contents"); + $r->print("".&mt('Navigate Course Contents').""); # ------------------------------------------------------------ Get query string &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['register']); @@ -221,7 +208,7 @@ sub real_handler { } } else { $r->print("" . - "Go To My First Homework Problem    "); + &mt("Go To My First Homework Problem")."    "); } my $suppressEmptySequences = 0; @@ -236,13 +223,13 @@ sub real_handler { $filterFunc = sub { my $res = shift; return $res->completable() || $res->is_map(); }; - $r->print("

Uncompleted Homework

"); + $r->print("

".&mt("Uncompleted Homework")."

"); $ENV{'form.filter'} = ''; $ENV{'form.condition'} = 1; $resource_no_folder_link = 1; } else { $r->print("" . - "Show Only Uncompleted Homework    "); + &mt("Show Only Uncompleted Homework")."    "); } # renderer call @@ -261,7 +248,7 @@ sub real_handler { # user knows there was no error. if ($renderArgs->{'counter'} == 0) { if ($showOnlyHomework) { - $r->print("

All homework is currently completed.

"); + $r->print("

".&mt("All homework is currently completed").".

"); } else { # both jumpToFirstHomework and normal use the same: course must be empty $r->print("

This course is empty.

"); } @@ -303,8 +290,16 @@ sub getLinkForResource { # Check to see if there are any pages in the stack foreach $res (@$stack) { - if (defined($res) && $res->is_page()) { - return $res->src(); + if (defined($res)) { + if ($res->is_page()) { + return $res->src(); + } + # in case folder was skipped over as "only sequence" + my ($map,$id,$src)=&Apache::lonnet::decode_symb($res->symb()); + if ($map=~/\.page$/) { + return &Apache::lonnet::clutter($map).'#'. + &Apache::lonnet::escape(&Apache::lonnet::declutter($src)); + } } } @@ -319,9 +314,9 @@ sub getLinkForResource { return $res->src(); } -# Convenience function: This seperates the logic of how to create +# Convenience function: This separates the logic of how to create # the problem text strings ("Due: DATE", "Open: DATE", "Not yet assigned", -# etc.) into a seperate function. It takes a resource object as the +# etc.) into a separate function. It takes a resource object as the # first parameter, and the part number of the resource as the second. # It's basically a big switch statement on the status of the resource. @@ -331,35 +326,35 @@ sub getDescription { my $status = $res->status($part); if ($status == $res->NETWORK_FAILURE) { - return "Having technical difficulties; please check status later"; + return &mt("Having technical difficulties; please check status later"); } if ($status == $res->NOTHING_SET) { - return "Not currently assigned."; + return &mt("Not currently assigned."); } if ($status == $res->OPEN_LATER) { return "Open " . timeToHumanString($res->opendate($part)); } if ($status == $res->OPEN) { if ($res->duedate($part)) { - return "Due " . timeToHumanString($res->duedate($part)); + return &mt("Due")." " .timeToHumanString($res->duedate($part)); } else { - return "Open, no due date"; + return &mt("Open, no due date"); } } if ($status == $res->PAST_DUE_ANSWER_LATER) { - return "Answer open " . timeToHumanString($res->answerdate($part)); + return &mt("Answer open")." " . timeToHumanString($res->answerdate($part)); } if ($status == $res->PAST_DUE_NO_ANSWER) { - return "Was due " . timeToHumanString($res->duedate($part)); + return &mt("Was due")." " . timeToHumanString($res->duedate($part)); } if ($status == $res->ANSWER_OPEN) { - return "Answer available"; + return &mt("Answer available"); } if ($status == $res->EXCUSED) { - return "Excused by instructor"; + return &mt("Excused by instructor"); } if ($status == $res->ATTEMPTED) { - return "Answer submitted, not yet graded."; + return &mt("Answer submitted, not yet graded"); } if ($status == $res->TRIES_LEFT) { my $tries = $res->tries($part); @@ -371,15 +366,15 @@ sub getDescription { $triesString = "$triesString"; } } - if ($res->duedate()) { - return "Due " . timeToHumanString($res->duedate($part)) . + if ($res->duedate($part)) { + return &mt("Due")." " . timeToHumanString($res->duedate($part)) . " $triesString"; } else { - return "No due date $triesString"; + return &mt("No due date")." $triesString"; } } if ($status == $res->ANSWER_SUBMITTED) { - return 'Answer submitted'; + return &mt('Answer submitted'); } } @@ -393,8 +388,8 @@ sub dueInLessThen24Hours { return ($status == $res->OPEN() || $status == $res->TRIES_LEFT()) && - $res->duedate() && $res->duedate() < time()+(24*60*60) && - $res->duedate() > time(); + $res->duedate($part) && $res->duedate($part) < time()+(24*60*60) && + $res->duedate($part) > time(); } # Convenience function, so others can use it: Is there only one try remaining for the @@ -406,8 +401,8 @@ sub lastTry { my $tries = $res->tries($part); my $maxtries = $res->maxtries($part); return $tries && $maxtries && $maxtries > 1 && - $maxtries - $tries == 1 && $res->duedate() && - $res->duedate() > time(); + $maxtries - $tries == 1 && $res->duedate($part) && + $res->duedate($part) > time(); } # This puts a human-readable name on the ENV variable. @@ -429,9 +424,11 @@ sub timeToHumanString { my ($time) = @_; # zero, '0' and blank are bad times if (!$time) { - return 'never'; + return &mt('never'); } - + unless (&Apache::lonlocal::current_language()=~/^en/) { + return &Apache::lonlocal::locallocaltime($time); + } my $now = time(); my @time = localtime($time); @@ -499,7 +496,7 @@ sub timeToHumanString { # HH:MM if ( $delta < $day * 5 ) { my $timeStr = strftime("%A, %b %e at %I:%M %P", localtime($time)); - $timeStr =~ s/12:00 am/midnight/; + $timeStr =~ s/12:00 am/00:00/; $timeStr =~ s/12:00 pm/noon/; return ($inPast ? "last " : "next ") . $timeStr; @@ -509,14 +506,14 @@ sub timeToHumanString { 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)); - $timeStr =~ s/12:00 am/midnight/; + $timeStr =~ s/12:00 am/00:00/; $timeStr =~ s/12:00 pm/noon/; return $timeStr; } # Not this year, so show the year my $timeStr = strftime("on %A, %b %e %G at %I:%M %P", localtime($time)); - $timeStr =~ s/12:00 am/midnight/; + $timeStr =~ s/12:00 am/00:00/; $timeStr =~ s/12:00 pm/noon/; return $timeStr; } @@ -893,12 +890,8 @@ sub render_resource { $icon = $params->{'indentString'}; } } else { - my $curfext= (split (/\./,$resource->src))[-1]; - my $embstyle = &Apache::loncommon::fileembstyle($curfext); - # The unless conditional that follows is a bit of overkill - if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) { - $icon = ""; - } + $icon = ""; } # Display the correct map icon to open or shut map @@ -968,7 +961,11 @@ sub render_resource { if ($resource->is_problem() && $part ne '0' && !$params->{'condensed'}) { - $partLabel = " (Part $part)"; + my $displaypart=&Apache::lonnet::EXT('resource.'.$part.'.display', + $resource->symb()); + unless ($displaypart) { $displaypart=$part; } + $partLabel = " (Part: $displaypart)"; + $link.='#'.&Apache::lonnet::escape($part); $title = ""; } @@ -1328,15 +1325,15 @@ sub render { $result.='Key:  '; if ($navmap->{LAST_CHECK}) { $result .= - ' New discussion since '. + ' '.&mt('New discussion since').' '. strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})). '  '. - ' New message (click to open)

'. + ' '.&mt('New message (click to open)').'

'. ''; } else { $result .= '  '. - ' Discussions'. - '   New message (click to open)'. + ' '.&mt('Discussions').''. + '   '.&mt('New message (click to open)'). ''; } @@ -1347,11 +1344,11 @@ sub render { if ($condition) { $result.="Close All Folders"; + "\">".&mt('Close All Folders').""; } else { $result.="Open All Folders"; + "\">".&mt('Open All Folders').""; } $result .= "

\n"; } @@ -1543,11 +1540,15 @@ sub render { my $filter = $it->{FILTER}; my $stack = $it->getStack(); my $src = getLinkForResource($stack); - + my $anchor=''; + if ($src=~s/(\#.*$)//) { + $anchor=$1; + } my $srcHasQuestion = $src =~ /\?/; $args->{"resourceLink"} = $src. ($srcHasQuestion?'&':'?') . - 'symb=' . &Apache::lonnet::escape($curRes->symb()); + 'symb=' . &Apache::lonnet::escape($curRes->symb()). + $anchor; # Now, display each column. foreach my $col (@$cols) { @@ -1584,7 +1585,6 @@ sub render { # If we have the connection, make sure the user is still connected my $c = $r->connection; if ($c->aborted()) { - Apache::lonnet::logthis("navmaps aborted"); # Who cares what we do, nobody will see it anyhow. return ''; } @@ -1599,7 +1599,12 @@ sub render { # it's quite likely this might fix other browsers, too, and # certainly won't hurt anything. if ($displayedJumpMarker) { - $result .= "\n"; + $result .= " +"; } $result .= ""; @@ -1975,7 +1980,7 @@ sub getById { sub getBySymb { my $self = shift; my $symb = shift; - my ($mapUrl, $id, $filename) = split (/___/, $symb); + my ($mapUrl, $id, $filename) = &Apache::lonnet::decode_symb($symb); my $map = $self->getResourceByUrl($mapUrl); return $self->getById($map->map_pc() . '.' . $id); } @@ -2101,7 +2106,11 @@ sub parmval_real { # ----------------------------------------------------- fourth , check default - my $default=&Apache::lonnet::metadata($fn,$rwhat.'.default'); + my $meta_rwhat=$rwhat; + $meta_rwhat=~s/\./_/g; + my $default=&Apache::lonnet::metadata($fn,$meta_rwhat); + if (defined($default)) { return $default} + $default=&Apache::lonnet::metadata($fn,'parameter_'.$meta_rwhat); if (defined($default)) { return $default} # --------------------------------------------------- fifth , cascade up parts @@ -2344,6 +2353,8 @@ consisting entirely of empty resources e ending resource, will cause a lot of BRANCH_STARTs and BRANCH_ENDs, but only one resource will be returned. +=back + =head2 Normal Usage Normal usage of the iterator object is to do the following: @@ -2364,8 +2375,6 @@ the depth of the iterator to see when it code. It is difficult to get right and harder to understand then this. They should be migrated to this new style. -=back - =cut # Here are the tokens for the iterator: @@ -2733,7 +2742,7 @@ package Apache::lonnavmaps::DFSiterator; # useful for pre-processing of some kind, and is in fact used by the main # iterator that way, but that's about it. # One could imagine merging this into the init routine of the main iterator, -# but this might as well be left seperate, since it is possible some other +# but this might as well be left separate, since it is possible some other # use might be found for it. - Jeremy # Unlike the main iterator, this DOES return all resources, even blank ones. @@ -3166,7 +3175,7 @@ sub is_page { sub is_problem { my $self=shift; my $src = $self->src(); - return ($src =~ /problem$/); + return ($src =~ /\.(problem|exam|quiz|assess|survey|form|library)$/); } sub is_sequence { my $self=shift; @@ -3366,7 +3375,7 @@ sub opendate { } sub problemstatus { (my $self, my $part) = @_; - return $self->parmval("problemstatus", $part); + return lc $self->parmval("problemstatus", $part); } sub sig { (my $self, my $part) = @_; @@ -3544,7 +3553,11 @@ sub responseType { my $part = shift; $self->extractParts(); - return $self->{RESPONSE_TYPES}->{$part}; + if (defined($self->{RESPONSE_TYPES}->{$part})) { + return @{$self->{RESPONSE_TYPES}->{$part}}; + } else { + return undef; + } } sub responseIds { @@ -3552,7 +3565,11 @@ sub responseIds { my $part = shift; $self->extractParts(); - return $self->{RESPONSE_IDS}->{$part}; + if (defined($self->{RESPONSE_IDS}->{$part})) { + return @{$self->{RESPONSE_IDS}->{$part}}; + } else { + return undef; + } } # Private function: Extracts the parts information, both part names and @@ -3569,32 +3586,44 @@ sub extractParts { # Retrieve part count, if this is a problem if ($self->is_problem()) { + my $partorder = &Apache::lonnet::metadata($self->src(), 'partorder'); my $metadata = &Apache::lonnet::metadata($self->src(), 'packages'); - if (!$metadata) { - $self->{RESOURCE_ERROR} = 1; - $self->{PARTS} = []; - $self->{PART_TYPE} = {}; - return; - } - foreach (split(/\,/,$metadata)) { - if ($_ =~ /^part_(.*)$/) { - my $part = $1; - # This floods the logs if it blows up - if (defined($parts{$part})) { - Apache::lonnet::logthis("$part multiply defined in metadata for " . $self->symb()); - } - # check to see if part is turned off. - - if (!Apache::loncommon::check_if_partid_hidden($part, $self->symb())) { - $parts{$part} = 1; - } - } + if ($partorder) { + my @parts; + for my $part (split (/,/,$partorder)) { + if (!Apache::loncommon::check_if_partid_hidden($part, $self->symb())) { + push @parts, $part; + $parts{$part} = 1; + } + } + $self->{PARTS} = \@parts; + } else { + if (!$metadata) { + $self->{RESOURCE_ERROR} = 1; + $self->{PARTS} = []; + $self->{PART_TYPE} = {}; + return; + } + foreach (split(/\,/,$metadata)) { + if ($_ =~ /^part_(.*)$/) { + my $part = $1; + # This floods the logs if it blows up + if (defined($parts{$part})) { + &Apache::lonnet::logthis("$part multiply defined in metadata for " . $self->symb()); + } + + # check to see if part is turned off. + + if (!Apache::loncommon::check_if_partid_hidden($part, $self->symb())) { + $parts{$part} = 1; + } + } + } + my @sortedParts = sort keys %parts; + $self->{PARTS} = \@sortedParts; } - - my @sortedParts = sort keys %parts; - $self->{PARTS} = \@sortedParts; my %responseIdHash; my %responseTypeHash; @@ -3606,7 +3635,7 @@ sub extractParts { } # Now, the unfortunate thing about this is that parts, part name, and - # response if are delimited by underscores, but both the part + # response id are delimited by underscores, but both the part # name and response id can themselves have underscores in them. # So we have to use our knowlege of part names to figure out # where the part names begin and end, and even then, it is possible @@ -3618,7 +3647,6 @@ sub extractParts { my $partIdSoFar = ''; my @partChunks = split /_/, $partStuff; my $i = 0; - for ($i = 0; $i < scalar(@partChunks); $i++) { if ($partIdSoFar) { $partIdSoFar .= '_'; } $partIdSoFar .= $partChunks[$i]; @@ -3626,13 +3654,11 @@ sub extractParts { my @otherChunks = @partChunks[$i+1..$#partChunks]; my $responseId = join('_', @otherChunks); push @{$responseIdHash{$partIdSoFar}}, $responseId; - $responseTypeHash{$partIdSoFar} = $responseType; - last; + push @{$responseTypeHash{$partIdSoFar}}, $responseType; } } } } - $self->{RESPONSE_IDS} = \%responseIdHash; $self->{RESPONSE_TYPES} = \%responseTypeHash; } @@ -3824,7 +3850,7 @@ sub getCompletionStatus { my $status = $self->queryRestoreHash('solved', shift); - # Left as seperate if statements in case we ever do more with this + # 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_override') {return $self->CORRECT_BY_OVERRIDE; } if ($status eq 'incorrect_attempted') {return $self->INCORRECT; } @@ -3945,7 +3971,12 @@ sub status { #if ($self->{RESOURCE_ERROR}) { return NETWORK_FAILURE; } if ($completionStatus == NETWORK_FAILURE) { return NETWORK_FAILURE; } - my $suppressFeedback = lc($self->parmval("problemstatus", $part)) eq 'no'; + 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()) { + $suppressFeedback = 0; + } # There are a few whole rows we can dispose of: if ($completionStatus == CORRECT ||