--- loncom/interface/lonnavmaps.pm 2003/06/18 15:14:23 1.207 +++ loncom/interface/lonnavmaps.pm 2005/09/29 17:14:14 1.341 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.207 2003/06/18 15:14:23 bowersj2 Exp $ +# $Id: lonnavmaps.pm,v 1.341 2005/09/29 17:14:14 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,29 +25,21 @@ # # 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; use strict; +use GDBM_File; 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 +use Time::HiRes qw( gettimeofday tv_interval ); # symbolic constants sub SYMB { return 1; } @@ -61,19 +53,15 @@ my $resObj = "Apache::lonnavmaps::resour # Keep these mappings in sync with lonquickgrades, which uses the colors # instead of the icons. my %statusIconMap = - ( $resObj->NETWORK_FAILURE => '', - $resObj->NOTHING_SET => '', - $resObj->CORRECT => 'navmap.correct.gif', - $resObj->EXCUSED => 'navmap.correct.gif', - $resObj->PAST_DUE_NO_ANSWER => 'navmap.wrong.gif', - $resObj->PAST_DUE_ANSWER_LATER => 'navmap.wrong.gif', - $resObj->ANSWER_OPEN => 'navmap.wrong.gif', - $resObj->OPEN_LATER => '', - $resObj->TRIES_LEFT => 'navmap.open.gif', - $resObj->INCORRECT => 'navmap.wrong.gif', - $resObj->OPEN => 'navmap.open.gif', - $resObj->ATTEMPTED => 'navmap.ellipsis.gif', - $resObj->ANSWER_SUBMITTED => '' ); + ( + $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 => '' + ); my %iconAltTags = ( 'navmap.correct.gif' => 'Correct', @@ -94,12 +82,69 @@ 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,$firsttime)=@_; + my $result; + if ($script ne 'no') { + $result.=''; + } + if ($mode eq 'link') { + &add_linkitem($toplinkitems,'launchnav','launch_navmapwin()', + "Launch navigation window"); + } + return $result; +} + +sub close { + if ($env{'environment.remotenavmap'} ne 'on') { return ''; } + return(< +window.status='Accessing Nav Control'; +menu=window.open("/adm/rat/empty.html","loncapanav", + "height=600,width=400,scrollbars=1"); +window.status='Closing Nav Control'; +menu.close(); +window.status='Done.'; + +ENDCLOSE +} + +sub update { + if ($env{'environment.remotenavmap'} ne 'on') { return ''; } + if (!$env{'request.course.id'}) { return ''; } + if ($ENV{'REQUEST_URI'}=~m|^/adm/navmaps|) { return ''; } + return(< + +ENDUPDATE +} + sub handler { my $r = shift; real_handler($r); @@ -107,67 +152,133 @@ sub handler { sub real_handler { my $r = shift; - + #my $t0=[&gettimeofday()]; # Handle header-only request if ($r->header_only) { - if ($ENV{'browser.mathml'}) { - $r->content_type('text/xml'); + if ($env{'browser.mathml'}) { + &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; } # Send header, don't cache this page - if ($ENV{'browser.mathml'}) { - $r->content_type('text/xml'); + if ($env{'browser.mathml'}) { + &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; - # Create the nav map - my $navmap = Apache::lonnavmaps::navmap->new( - $ENV{"request.course.fn"}.".db", - $ENV{"request.course.fn"}."_parms.db", 1, 1); + my %toplinkitems=(); + &add_linkitem(\%toplinkitems,'blank','',"Select Action"); + if ($ENV{QUERY_STRING} eq 'collapseExternal') { + &Apache::lonnet::put('environment',{'remotenavmap' => 'off'}); + &Apache::lonnet::appenv('environment.remotenavmap' => 'off'); + my $menu=&Apache::lonmenu::reopenmenu(); + my $navstatus=&Apache::lonmenu::get_nav_status(); + if ($menu) { + $menu=(<print(<<"ENDSUBM"); + $html + + + + + +ENDSUBM + return; + } + if ($ENV{QUERY_STRING} =~ /^launchExternal/) { + &Apache::lonnet::put('environment',{'remotenavmap' => 'on'}); + &Apache::lonnet::appenv('environment.remotenavmap' => 'on'); + my $menu=&Apache::lonmenu::reopenmenu(); + my $navstatus=&Apache::lonmenu::get_nav_status(); + if ($menu) { + $r->print(< + swmenu=$menu + swmenu.clearTimeout(swmenu.menucltim); + $navstatus + +MENU + } + } + if ($ENV{QUERY_STRING} eq 'turningOffExternal') { + $env{'environment.remotenavmap'}='off'; + } + # 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"; + $env{'user.error.msg'} = "$requrl:bre:0:0:Course not initialized"; return HTTP_NOT_ACCEPTABLE; } - - $r->print("\n"); - $r->print("Navigate Course Contents"); + my $html=&Apache::lonxml::xmlbegin(); + $r->print("$html\n"); + $r->print("".&mt('Navigate Course Contents').""); # ------------------------------------------------------------ Get query string - &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['register']); + &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['register','sort','showOnlyHomework','postsymb']); # ----------------------------------------------------- Force menu registration my $addentries=''; - if ($ENV{'form.register'}) { - $addentries=' onLoad="'.&Apache::lonmenu::loadevents(). - '" onUnload="'.&Apache::lonmenu::unloadevents().'"'; - $r->print(&Apache::lonmenu::registerurl(1)); + my $more_unload; + my $body_only=''; + if ($env{'environment.remotenavmap'} eq 'on') { + $r->print(''); +# FIXME need to be smarter to only catch window close events +# $more_unload="collapse()" + $body_only=1; + } + if ($env{'form.register'}) { + $addentries=' onLoad="'.&Apache::lonmenu::loadevents(). + '" onUnload="'.&Apache::lonmenu::unloadevents().';'. + $more_unload.'"'; + $r->print(&Apache::lonmenu::registerurl(1)); + } else { + $addentries=' onUnload="'.$more_unload.'"'; } # Header $r->print(''. &Apache::loncommon::bodytag('Navigate Course Contents','', - $addentries,'','',$ENV{'form.register'})); + $addentries,$body_only,'', + $env{'form.register'})); $r->print(''); - - $r->rflush(); - - # Now that we've displayed some stuff to the user, init the navmap - $navmap->init(); - + $r->rflush(); # Check that it's defined if (!($navmap->courseMapDefined())) { + $r->print(&Apache::loncommon::help_open_menu('','Navigation Screen','Navigation_Screen','',undef,'RAT')); $r->print('Coursemap undefined.' . ''); return OK; @@ -175,50 +286,58 @@ sub real_handler { # See if there's only one map in the top-level, if we don't # already have a filter... if so, automatically display it + # (older code; should use retrieveResources) if ($ENV{QUERY_STRING} !~ /filter/) { my $iterator = $navmap->getIterator(undef, undef, undef, 0); - my $depth = 1; - $iterator->next(); - my $curRes = $iterator->next(); + my $curRes; my $sequenceCount = 0; my $sequenceId; - while ($depth > 0) { - if ($curRes == $iterator->BEGIN_MAP()) { $depth++; } - if ($curRes == $iterator->END_MAP()) { $depth--; } - + while ($curRes = $iterator->next()) { if (ref($curRes) && $curRes->is_sequence()) { $sequenceCount++; $sequenceId = $curRes->map_pc(); } - - $curRes = $iterator->next(); } 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(' + '); + } + + if ($env{'environment.remotenavmap'} ne 'on') { + $r->print(&launch_win('link','yes',\%toplinkitems)); + } + if ($env{'environment.remotenavmap'} eq 'on') { + &add_linkitem(\%toplinkitems,'closenav','collapse()', + "Close navigation window"); + } + my $jumpToFirstHomework = 0; # Check to see if the student is jumping to next open, do-able problem - if ($ENV{QUERY_STRING} eq 'jumpToFirstHomework') { + if ($ENV{QUERY_STRING} =~ /^jumpToFirstHomework/) { $jumpToFirstHomework = 1; # Find the next homework problem that they can do. my $iterator = $navmap->getIterator(undef, undef, undef, 1); - my $depth = 1; - $iterator->next(); - my $curRes = $iterator->next(); + my $curRes; my $foundDoableProblem = 0; my $problemRes; - while ($depth > 0 && !$foundDoableProblem) { - if ($curRes == $iterator->BEGIN_MAP()) { $depth++; } - if ($curRes == $iterator->END_MAP()) { $depth--; } - + while (($curRes = $iterator->next()) && !$foundDoableProblem) { if (ref($curRes) && $curRes->is_problem()) { my $status = $curRes->status(); if ($curRes->completable()) { @@ -230,14 +349,12 @@ sub real_handler { 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(); } } - } continue { - $curRes = $iterator->next(); } # If we found no problems, print a note to that effect. @@ -245,8 +362,9 @@ sub real_handler { $r->print("All homework assignments have been completed.

"); } } else { - $r->print("" . - "Go To My First Homework Problem    "); + &add_linkitem(\%toplinkitems,'firsthomework', + 'location.href="navmaps?jumpToFirstHomework"', + "Show Me My First Homework Problem"); } my $suppressEmptySequences = 0; @@ -255,42 +373,65 @@ sub real_handler { # Display only due homework. my $showOnlyHomework = 0; - if ($ENV{QUERY_STRING} eq 'showOnlyHomework') { + if ($env{'form.showOnlyHomework'} eq "1") { $showOnlyHomework = 1; $suppressEmptySequences = 1; $filterFunc = sub { my $res = shift; return $res->completable() || $res->is_map(); }; - $r->print("

Uncompleted Homework

"); - $ENV{'form.filter'} = ''; - $ENV{'form.condition'} = 1; + &add_linkitem(\%toplinkitems,'everything', + 'location.href="navmaps?sort='.$env{'form.sort'}.'"', + "Show Everything"); + $r->print("

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

"); + $env{'form.filter'} = ''; + $env{'form.condition'} = 1; $resource_no_folder_link = 1; } else { - $r->print("" . - "Show Only Uncompleted Homework    "); - } - + &add_linkitem(\%toplinkitems,'uncompleted', + 'location.href="navmaps?sort='.$env{'form.sort'}. + '&showOnlyHomework=1"', + "Show Only Uncompleted Homework"); + } + + my %selected=($env{'form.sort'} => 'selected=on'); + my $sort_html=("
+ + + + + +
"); # renderer call my $renderArgs = { 'cols' => [0,1,2,3], + 'sort' => $env{'form.sort'}, 'url' => '/adm/navmaps', 'navmap' => $navmap, 'suppressNavmap' => 1, '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. 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.

"); } } + #my $td=&tv_interval($t0); + #$r->print("
$td"); $r->print(""); $r->rflush(); @@ -321,15 +462,26 @@ sub removeFromFilter { # Convenience function: Given a stack returned from getStack on the iterator, # return the correct src() value. -# Later, this should add an anchor when we start putting anchors in pages. sub getLinkForResource { my $stack = shift; my $res; # 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)) { + my $anchor; + if ($res->is_page()) { + foreach (@$stack) { if (defined($_)) { $anchor = $_; } } + $anchor=&Apache::lonnet::escape($anchor->shown_symb()); + return ($res->link(),$res->shown_symb(),$anchor); + } + # in case folder was skipped over as "only sequence" + my ($map,$id,$src)=&Apache::lonnet::decode_symb($res->symb()); + if ($map=~/\.page$/) { + my $url=&Apache::lonnet::clutter($map); + $anchor=&Apache::lonnet::escape($src->shown_symb()); + return ($url,$res->shown_symb(),$anchor); + } } } @@ -341,12 +493,12 @@ sub getLinkForResource { if (defined($_)) { $res = $_; } } - return $res->src(); + return ($res->link(),$res->shown_symb()); } -# 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. @@ -356,35 +508,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)); + return "Open " . timeToHumanString($res->opendate($part),'start'); } if ($status == $res->OPEN) { if ($res->duedate($part)) { - return "Due " . timeToHumanString($res->duedate($part)); + return &mt("Due")." " .timeToHumanString($res->duedate($part),'end'); } 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),'start'); } if ($status == $res->PAST_DUE_NO_ANSWER) { - return "Was due " . timeToHumanString($res->duedate($part)); + return &mt("Was due")." " . timeToHumanString($res->duedate($part),'end'); } - if ($status == $res->ANSWER_OPEN) { - return "Answer available"; + if ($status == $res->ANSWER_OPEN || $status == $res->PARTIALLY_CORRECT) { + 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); @@ -396,30 +548,30 @@ sub getDescription { $triesString = "$triesString"; } } - if ($res->duedate()) { - return "Due " . timeToHumanString($res->duedate($part)) . + if ($res->duedate($part)) { + return &mt("Due")." " . timeToHumanString($res->duedate($part),'end') . " $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'); } } # Convenience function, so others can use it: Is the problem due in less then # 24 hours, and still can be done? -sub dueInLessThen24Hours { +sub dueInLessThan24Hours { my $res = shift; my $part = shift; my $status = $res->status($part); 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 @@ -431,14 +583,14 @@ 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. +# This puts a human-readable name on the env variable. sub advancedUser { - return $ENV{'request.role.adv'}; + return $env{'request.role.adv'}; } @@ -451,12 +603,14 @@ sub advancedUser { # Very, very, very, VERY English-only... goodness help a localizer on # this func... sub timeToHumanString { - my ($time) = @_; + my ($time,$type) = @_; # 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); @@ -524,24 +678,31 @@ 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 ") . + return ($inPast ? "last " : "this ") . $timeStr; } + my $conjunction='on'; + if ($type eq 'start') { + $conjunction='at'; + } elsif ($type eq 'end') { + $conjunction='by'; + } + &Apache::lonnet::logthis("

$conjunction $type

"); # 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)); - $timeStr =~ s/12:00 am/midnight/; + 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; } # 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/; + 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; } @@ -552,7 +713,8 @@ sub timeToHumanString { =head1 NAME -Apache::lonnavmap - Subroutines to handle and render the navigation maps +Apache::lonnavmap - Subroutines to handle and render the navigation + maps =head1 SYNOPSIS @@ -560,18 +722,62 @@ The main handler generates the navigatio the other objects export this information in a usable fashion for other modules. +=head1 OVERVIEW + +X When a user enters a course, LON-CAPA examines the +course structure and caches it in what is often referred to as the +"big hash" X. You can see it if you are logged into +LON-CAPA, in a course, by going to /adm/test. (You may need to +tweak the /home/httpd/lonTabs/htpasswd file to view it.) The +content of the hash will be under the heading "Big Hash". + +Big Hash contains, among other things, how resources are related +to each other (next/previous), what resources are maps, which +resources are being chosen to not show to the student (for random +selection), and a lot of other things that can take a lot of time +to compute due to the amount of data that needs to be collected and +processed. + +Apache::lonnavmaps provides an object model for manipulating this +information in a higher-level fashion then directly manipulating +the hash. It also provides access to several auxilary functions +that aren't necessarily stored in the Big Hash, but are a per- +resource sort of value, like whether there is any feedback on +a given resource. + +Apache::lonnavmaps also abstracts away branching, and someday, +conditions, for the times where you don't really care about those +things. + +Apache::lonnavmaps also provides fairly powerful routines for +rendering navmaps, and last but not least, provides the navmaps +view for when the user clicks the NAV button. + +B: Apache::lonnavmaps I works for the "currently +logged in user"; if you want things like "due dates for another +student" lonnavmaps can not directly retrieve information like +that. You need the EXT function. This module can still help, +because many things, such as the course structure, are constant +between users, and Apache::lonnavmaps can help by providing +symbs for the EXT call. + +The rest of this file will cover the provided rendering routines, +which can often be used without fiddling with the navmap object at +all, then documents the Apache::lonnavmaps::navmap object, which +is the key to accessing the Big Hash information, covers the use +of the Iterator (which provides the logic for traversing the +somewhat-complicated Big Hash data structure), documents the +Apache::lonnavmaps::Resource objects that are returned by + =head1 Subroutine: render The navmap renderer package provides a sophisticated rendering of the standard navigation maps interface into HTML. The provided nav map handler is actually just a glorified call to this. -Because of the large number of parameters this function presents, +Because of the large number of parameters this function accepts, instead of passing it arguments as is normal, pass it in an anonymous -hash with the given options. This is because there is no obvious order -you may wish to override these in and a hash is easier to read and -understand then "undef, undef, undef, 1, undef, undef, renderButton, -undef, 0" when you mostly want default behaviors. +hash with the desired options. The package provides a function called 'render', called as Apache::lonnavmaps::render({}). @@ -599,29 +805,33 @@ that takes a resource reference, a part argument hash passed to the renderer, and returns a string that will be inserted into the HTML representation as it. +All other parameters are ways of either changing how the columns +are printing, or which rows are shown. + The pre-packaged column names are refered to by constants in the Apache::lonnavmaps namespace. The following currently exist: =over 4 -=item * B: +=item * B: The general info about the resource: Link, icon for the type, etc. The -first column in the standard nav map display. This column also accepts +first column in the standard nav map display. This column provides the +indentation effect seen in the B