version 1.415.2.2, 2008/12/12 22:27:20
|
version 1.438, 2009/11/02 12:10:34
|
Line 2
|
Line 2
|
# Navigate Maps Handler |
# Navigate Maps Handler |
# |
# |
# $Id$ |
# $Id$ |
|
|
# |
# |
# Copyright Michigan State University Board of Trustees |
# Copyright Michigan State University Board of Trustees |
# |
# |
Line 27
|
Line 28
|
# |
# |
### |
### |
|
|
|
=pod |
|
|
|
=head1 NAME |
|
|
|
Apache::lonnavmaps - Subroutines to handle and render the navigation |
|
|
|
=head1 SYNOPSIS |
|
|
|
Handles navigational maps. |
|
|
|
The main handler generates the navigational listing for the course, |
|
the other objects export this information in a usable fashion for |
|
other modules. |
|
|
|
|
|
This is part of the LearningOnline Network with CAPA project |
|
described at http://www.lon-capa.org. |
|
|
|
|
|
=head1 OVERVIEW |
|
|
|
X<lonnavmaps, overview 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<big hash>. 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 than 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<Note>: Apache::lonnavmaps I<only> 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 accepts, |
|
instead of passing it arguments as is normal, pass it in an anonymous |
|
hash with the desired options. |
|
|
|
The package provides a function called 'render', called as |
|
Apache::lonnavmaps::render({}). |
|
|
|
=head2 Overview of Columns |
|
|
|
The renderer will build an HTML table for the navmap and return |
|
it. The table consists of several columns, and a row for each |
|
resource (or possibly each part). You tell the renderer how many |
|
columns to create and what to place in each column, optionally using |
|
one or more of the prepared columns, and the renderer will assemble |
|
the table. |
|
|
|
Any additional generally useful column types should be placed in the |
|
renderer code here, so anybody can use it anywhere else. Any code |
|
specific to the current application (such as the addition of <input> |
|
elements in a column) should be placed in the code of the thing using |
|
the renderer. |
|
|
|
At the core of the renderer is the array reference COLS (see Example |
|
section below for how to pass this correctly). The COLS array will |
|
consist of entries of one of two types of things: Either an integer |
|
representing one of the pre-packaged column types, or a sub reference |
|
that takes a resource reference, a part number, and a reference to the |
|
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<Apache::lonnavmaps::resource>: |
|
|
|
The general info about the resource: Link, icon for the type, etc. The |
|
first column in the standard nav map display. This column provides the |
|
indentation effect seen in the B<NAV> screen. This column also accepts |
|
the following parameters in the renderer hash: |
|
|
|
=over 4 |
|
|
|
=item * B<resource_nolink>: default false |
|
|
|
If true, the resource will not be linked. By default, all non-folder |
|
resources are linked. |
|
|
|
=item * B<resource_part_count>: default true |
|
|
|
If true, the resource will show a part count B<if> the full |
|
part list is not displayed. (See "condense_parts" later.) If false, |
|
the resource will never show a part count. |
|
|
|
=item * B<resource_no_folder_link>: |
|
|
|
If true, the resource's folder will not be clickable to open or close |
|
it. Default is false. True implies printCloseAll is false, since you |
|
can't close or open folders when this is on anyhow. |
|
|
|
=back |
|
|
|
=item * B<Apache::lonnavmaps::communication_status>: |
|
|
|
Whether there is discussion on the resource, email for the user, or |
|
(lumped in here) perl errors in the execution of the problem. This is |
|
the second column in the main nav map. |
|
|
|
=item * B<Apache::lonnavmaps::quick_status>: |
|
|
|
An icon for the status of a problem, with five possible states: |
|
Correct, incorrect, open, awaiting grading (for a problem where the |
|
computer's grade is suppressed, or the computer can't grade, like |
|
essay problem), or none (not open yet, not a problem). The |
|
third column of the standard navmap. |
|
|
|
=item * B<Apache::lonnavmaps::long_status>: |
|
|
|
A text readout of the details of the current status of the problem, |
|
such as "Due in 22 hours". The fourth column of the standard navmap. |
|
|
|
=item * B<Apache::lonnavmaps::part_status_summary>: |
|
|
|
A text readout summarizing the status of the problem. If it is a |
|
single part problem, will display "Correct", "Incorrect", |
|
"Not yet open", "Open", "Attempted", or "Error". If there are |
|
multiple parts, this will output a string that in HTML will show a |
|
status of how many parts are in each status, in color coding, trying |
|
to match the colors of the icons within reason. |
|
|
|
Note this only makes sense if you are I<not> showing parts. If |
|
C<showParts> is true (see below), this column will not output |
|
anything. |
|
|
|
=back |
|
|
|
If you add any others please be sure to document them here. |
|
|
|
An example of a column renderer that will show the ID number of a |
|
resource, along with the part name if any: |
|
|
|
sub { |
|
my ($resource, $part, $params) = @_; |
|
if ($part) { return '<td>' . $resource->{ID} . ' ' . $part . '</td>'; } |
|
return '<td>' . $resource->{ID} . '</td>'; |
|
} |
|
|
|
Note these functions are responsible for the TD tags, which allow them |
|
to override vertical and horizontal alignment, etc. |
|
|
|
=head2 Parameters |
|
|
|
Minimally, you should be |
|
able to get away with just using 'cols' (to specify the columns |
|
shown), 'url' (necessary for the folders to link to the current screen |
|
correctly), and possibly 'queryString' if your app calls for it. In |
|
that case, maintaining the state of the folders will be done |
|
automatically. |
|
|
|
=over 4 |
|
|
|
=item * B<iterator>: default: constructs one from %env |
|
|
|
A reference to a fresh ::iterator to use from the navmaps. The |
|
rendering will reflect the options passed to the iterator, so you can |
|
use that to just render a certain part of the course, if you like. If |
|
one is not passed, the renderer will attempt to construct one from |
|
env{'form.filter'} and env{'form.condition'} information, plus the |
|
'iterator_map' parameter if any. |
|
|
|
=item * B<iterator_map>: default: not used |
|
|
|
If you are letting the renderer do the iterator handling, you can |
|
instruct the renderer to render only a particular map by passing it |
|
the source of the map you want to process, like |
|
'/res/103/jerf/navmap.course.sequence'. |
|
|
|
=item * B<include_top_level_map>: default: false |
|
|
|
If you need to include the top level map (meaning the course) in the |
|
rendered output set this to true |
|
|
|
=item * B<navmap>: default: constructs one from %env |
|
|
|
A reference to a navmap, used only if an iterator is not passed in. If |
|
this is necessary to make an iterator but it is not passed in, a new |
|
one will be constructed based on env info. This is useful to do basic |
|
error checking before passing it off to render. |
|
|
|
=item * B<r>: default: must be passed in |
|
|
|
The standard Apache response object. This must be passed to the |
|
renderer or the course hash will be locked. |
|
|
|
=item * B<cols>: default: empty (useless) |
|
|
|
An array reference |
|
|
|
=item * B<showParts>:default true |
|
|
|
A flag. If true, a line for the resource itself, and a line |
|
for each part will be displayed. If not, only one line for each |
|
resource will be displayed. |
|
|
|
=item * B<condenseParts>: default true |
|
|
|
A flag. If true, if all parts of the problem have the same |
|
status and that status is Nothing Set, Correct, or Network Failure, |
|
then only one line will be displayed for that resource anyhow. If no, |
|
all parts will always be displayed. If showParts is 0, this is |
|
ignored. |
|
|
|
=item * B<jumpCount>: default: determined from %env |
|
|
|
A string identifying the URL to place the anchor 'curloc' at. |
|
It is the responsibility of the renderer user to |
|
ensure that the #curloc is in the URL. By default, determined through |
|
the use of the env{} 'jump' information, and should normally "just |
|
work" correctly. |
|
|
|
=item * B<here>: default: empty string |
|
|
|
A Symb identifying where to place the 'here' marker. The empty |
|
string means no marker. |
|
|
|
=item * B<indentString>: default: 25 pixel whitespace image |
|
|
|
A string identifying the indentation string to use. |
|
|
|
=item * B<queryString>: default: empty |
|
|
|
A string which will be prepended to the query string used when the |
|
folders are opened or closed. You can use this to pass |
|
application-specific values. |
|
|
|
=item * B<url>: default: none |
|
|
|
The url the folders will link to, which should be the current |
|
page. Required if the resource info column is shown, and you |
|
are allowing the user to open and close folders. |
|
|
|
=item * B<currentJumpIndex>: default: no jumping |
|
|
|
Describes the currently-open row number to cause the browser to jump |
|
to, because the user just opened that folder. By default, pulled from |
|
the Jump information in the env{'form.*'}. |
|
|
|
=item * B<printKey>: default: false |
|
|
|
If true, print the key that appears on the top of the standard |
|
navmaps. |
|
|
|
=item * B<printCloseAll>: default: true |
|
|
|
If true, print the "Close all folders" or "open all folders" |
|
links. |
|
|
|
=item * B<filterFunc>: default: sub {return 1;} (accept everything) |
|
|
|
A function that takes the resource object as its only parameter and |
|
returns a true or false value. If true, the resource is displayed. If |
|
false, it is simply skipped in the display. |
|
|
|
=item * B<suppressEmptySequences>: default: false |
|
|
|
If you're using a filter function, and displaying sequences to orient |
|
the user, then frequently some sequences will be empty. Setting this to |
|
true will cause those sequences not to display, so as not to confuse the |
|
user into thinking that if the sequence is there there should be things |
|
under it; for example, see the "Show Uncompleted Homework" view on the |
|
B<NAV> screen. |
|
|
|
=item * B<suppressNavmaps>: default: false |
|
|
|
If true, will not display Navigate Content resources. |
|
|
|
=back |
|
|
|
=head2 Additional Info |
|
|
|
In addition to the parameters you can pass to the renderer, which will |
|
be passed through unchange to the column renderers, the renderer will |
|
generate the following information which your renderer may find |
|
useful: |
|
|
|
=over 4 |
|
|
|
=item * B<counter>: |
|
|
|
Contains the number of rows printed. Useful after calling the render |
|
function, as you can detect whether anything was printed at all. |
|
|
|
=item * B<isNewBranch>: |
|
|
|
Useful for renderers: If this resource is currently the first resource |
|
of a new branch, this will be true. The Resource column (leftmost in the |
|
navmaps screen) uses this to display the "new branch" icon |
|
|
|
=back |
|
|
|
=cut |
|
|
|
|
|
=head1 SUBROUTINES |
|
|
|
=over |
|
|
|
=item update() |
|
|
|
=item addToFilter() |
|
|
|
Convenience functions: Returns a string that adds or subtracts |
|
the second argument from the first hash, appropriate for the |
|
query string that determines which folders to recurse on |
|
|
|
=item removeFromFilter() |
|
|
|
=item getLinkForResource() |
|
|
|
Convenience function: Given a stack returned from getStack on the iterator, |
|
return the correct src() value. |
|
|
|
=item getDescription() |
|
|
|
Convenience function: This separates the logic of how to create |
|
the problem text strings ("Due: DATE", "Open: DATE", "Not yet assigned", |
|
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. |
|
|
|
=item dueInLessThan24Hours() |
|
|
|
Convenience function, so others can use it: Is the problem due in less than 24 hours, and still can be done? |
|
|
|
=item lastTry() |
|
|
|
Convenience function, so others can use it: Is there only one try remaining for the |
|
part, with more than one try to begin with, not due yet and still can be done? |
|
|
|
=item advancedUser() |
|
|
|
This puts a human-readable name on the env variable. |
|
|
|
=item timeToHumanString() |
|
|
|
timeToHumanString takes a time number and converts it to a |
|
human-readable representation, meant to be used in the following |
|
manner: |
|
|
|
=over 4 |
|
|
|
=item * print "Due $timestring" |
|
|
|
=item * print "Open $timestring" |
|
|
|
=item * print "Answer available $timestring" |
|
|
|
=back |
|
|
|
Very, very, very, VERY English-only... goodness help a localizer on |
|
this func... |
|
|
|
=item resource() |
|
|
|
returns 0 |
|
|
|
=item communication_status() |
|
|
|
returns 1 |
|
|
|
=item quick_status() |
|
|
|
returns 2 |
|
|
|
=item long_status() |
|
|
|
returns 3 |
|
|
|
=item part_status_summary() |
|
|
|
returns 4 |
|
|
|
=item render_resource() |
|
|
|
=item render_communication_status() |
|
|
|
=item render_quick_status() |
|
|
|
=item render_long_status() |
|
|
|
=item render_parts_summary_status() |
|
|
|
=item setDefault() |
|
|
|
=item cmp_title() |
|
|
|
=item render() |
|
|
|
=item add_linkitem() |
|
|
|
=item show_linkitems() |
|
|
|
=back |
|
|
|
=cut |
|
|
package Apache::lonnavmaps; |
package Apache::lonnavmaps; |
|
|
use strict; |
use strict; |
Line 62 my %statusIconMap =
|
Line 505 my %statusIconMap =
|
$resObj->ERROR => '' |
$resObj->ERROR => '' |
); |
); |
|
|
my %iconAltTags = |
my %iconAltTags = #texthash does not work here |
( 'navmap.correct.gif' => 'Correct', |
( 'navmap.correct.gif' => 'Correct', |
'navmap.wrong.gif' => 'Incorrect', |
'navmap.wrong.gif' => 'Incorrect', |
'navmap.open.gif' => 'Open' ); |
'navmap.open.gif' => 'Open', |
|
'navmap.partial.gif' => 'Partially Correct', ); |
|
|
# Defines a status->color mapping, null string means don't color |
# Defines a status->color mapping, null string means don't color |
my %colormap = |
my %colormap = |
Line 88 my %colormap =
|
Line 532 my %colormap =
|
# is not yet done and due in less than 24 hours |
# is not yet done and due in less than 24 hours |
my $hurryUpColor = "#FF0000"; |
my $hurryUpColor = "#FF0000"; |
|
|
|
my $future_slots_checked = 0; |
|
my $future_slots = 0; |
|
|
sub close { |
sub close { |
if ($env{'environment.remotenavmap'} ne 'on') { return ''; } |
if ($env{'environment.remotenavmap'} ne 'on') { return ''; } |
return(<<ENDCLOSE); |
return(<<ENDCLOSE); |
Line 116 this.document.navform.submit();
|
Line 563 this.document.navform.submit();
|
ENDUPDATE |
ENDUPDATE |
} |
} |
|
|
# Convenience functions: Returns a string that adds or subtracts |
|
# the second argument from the first hash, appropriate for the |
|
# query string that determines which folders to recurse on |
|
sub addToFilter { |
sub addToFilter { |
my $hashIn = shift; |
my $hashIn = shift; |
my $addition = shift; |
my $addition = shift; |
Line 137 sub removeFromFilter {
|
Line 582 sub removeFromFilter {
|
return join(",", keys(%hash)); |
return join(",", keys(%hash)); |
} |
} |
|
|
# Convenience function: Given a stack returned from getStack on the iterator, |
|
# return the correct src() value. |
|
sub getLinkForResource { |
sub getLinkForResource { |
my $stack = shift; |
my $stack = shift; |
my $res; |
my $res; |
Line 156 sub getLinkForResource {
|
Line 599 sub getLinkForResource {
|
my ($map,$id,$src)=&Apache::lonnet::decode_symb($res->symb()); |
my ($map,$id,$src)=&Apache::lonnet::decode_symb($res->symb()); |
if ($map=~/\.page$/) { |
if ($map=~/\.page$/) { |
my $url=&Apache::lonnet::clutter($map); |
my $url=&Apache::lonnet::clutter($map); |
$anchor=&escape($src->shown_symb()); |
$anchor=&escape($res->shown_symb()); |
return ($url,$res->shown_symb(),$anchor); |
return ($url,$res->shown_symb(),$anchor); |
} |
} |
} |
} |
Line 176 sub getLinkForResource {
|
Line 619 sub getLinkForResource {
|
return; |
return; |
} |
} |
|
|
# Convenience function: This separates the logic of how to create |
|
# the problem text strings ("Due: DATE", "Open: DATE", "Not yet assigned", |
|
# 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. |
|
|
|
sub getDescription { |
sub getDescription { |
my $res = shift; |
my $res = shift; |
Line 200 sub getDescription {
|
Line 639 sub getDescription {
|
if ($status == $res->OPEN_LATER) { |
if ($status == $res->OPEN_LATER) { |
return &mt("Open ") .timeToHumanString($open,'start'); |
return &mt("Open ") .timeToHumanString($open,'start'); |
} |
} |
|
if ($res->simpleStatus($part) == $res->OPEN) { |
|
unless (&Apache::lonnet::allowed('mgr',$env{'request.course.id'})) { |
|
my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part); |
|
if ($slot_status == $res->UNKNOWN) { |
|
return &mt('Reservation status unknown'); |
|
} elsif ($slot_status == $res->RESERVED) { |
|
return &mt('Reserved - ends [_1]', |
|
timeToHumanString($slot_time,'end')); |
|
} elsif ($slot_status == $res->RESERVED_LOCATION) { |
|
return &mt('Reserved - specific location(s) - ends [_1]', |
|
timeToHumanString($slot_time,'end')); |
|
} elsif ($slot_status == $res->RESERVED_LATER) { |
|
return &mt('Reserved - next open [_1]', |
|
timeToHumanString($slot_time,'start')); |
|
} elsif ($slot_status == $res->RESERVABLE) { |
|
return &mt('Reservable ending [_1]', |
|
timeToHumanString($slot_time,'end')); |
|
} elsif ($slot_status == $res->RESERVABLE_LATER) { |
|
return &mt('Reservable starting [_1]', |
|
timeToHumanString($slot_time,'start')); |
|
} elsif ($slot_status == $res->NOT_IN_A_SLOT) { |
|
return &mt('Reserve a time/place to work'); |
|
} elsif ($slot_status == $res->NOTRESERVABLE) { |
|
return &mt('Reservation not available'); |
|
} elsif ($slot_status == $res->WAITING_FOR_GRADE) { |
|
return &mt('Submission in grading queue'); |
|
} |
|
} |
|
} |
if ($status == $res->OPEN) { |
if ($status == $res->OPEN) { |
if ($due) { |
if ($due) { |
if ($res->is_practice()) { |
if ($res->is_practice()) { |
Line 253 sub getDescription {
|
Line 721 sub getDescription {
|
} |
} |
} |
} |
|
|
# Convenience function, so others can use it: Is the problem due in less than |
|
# 24 hours, and still can be done? |
|
|
|
sub dueInLessThan24Hours { |
sub dueInLessThan24Hours { |
my $res = shift; |
my $res = shift; |
Line 267 sub dueInLessThan24Hours {
|
Line 733 sub dueInLessThan24Hours {
|
$res->duedate($part) > time(); |
$res->duedate($part) > time(); |
} |
} |
|
|
# Convenience function, so others can use it: Is there only one try remaining for the |
|
# part, with more than one try to begin with, not due yet and still can be done? |
|
sub lastTry { |
sub lastTry { |
my $res = shift; |
my $res = shift; |
my $part = shift; |
my $part = shift; |
Line 280 sub lastTry {
|
Line 745 sub lastTry {
|
$res->duedate($part) > time(); |
$res->duedate($part) > time(); |
} |
} |
|
|
# This puts a human-readable name on the env variable. |
|
|
|
sub advancedUser { |
sub advancedUser { |
return $env{'request.role.adv'}; |
return $env{'request.role.adv'}; |
} |
} |
|
|
|
|
# timeToHumanString takes a time number and converts it to a |
|
# human-readable representation, meant to be used in the following |
|
# manner: |
|
# print "Due $timestring" |
|
# print "Open $timestring" |
|
# print "Answer available $timestring" |
|
# Very, very, very, VERY English-only... goodness help a localizer on |
|
# this func... |
|
|
|
|
|
sub timeToHumanString { |
sub timeToHumanString { |
my ($time,$type,$format) = @_; |
my ($time,$type,$format) = @_; |
|
|
Line 415 sub timeToHumanString {
|
Line 868 sub timeToHumanString {
|
} |
} |
|
|
|
|
=pod |
|
|
|
=head1 NAME |
|
|
|
Apache::lonnavmap - Subroutines to handle and render the navigation |
|
maps |
|
|
|
=head1 SYNOPSIS |
|
|
|
The main handler generates the navigational listing for the course, |
|
the other objects export this information in a usable fashion for |
|
other modules. |
|
|
|
=head1 OVERVIEW |
|
|
|
X<lonnavmaps, overview> 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<big hash>. 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 than 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<Note>: Apache::lonnavmaps I<only> 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 accepts, |
|
instead of passing it arguments as is normal, pass it in an anonymous |
|
hash with the desired options. |
|
|
|
The package provides a function called 'render', called as |
|
Apache::lonnavmaps::render({}). |
|
|
|
=head2 Overview of Columns |
|
|
|
The renderer will build an HTML table for the navmap and return |
|
it. The table consists of several columns, and a row for each |
|
resource (or possibly each part). You tell the renderer how many |
|
columns to create and what to place in each column, optionally using |
|
one or more of the prepared columns, and the renderer will assemble |
|
the table. |
|
|
|
Any additional generally useful column types should be placed in the |
|
renderer code here, so anybody can use it anywhere else. Any code |
|
specific to the current application (such as the addition of <input> |
|
elements in a column) should be placed in the code of the thing using |
|
the renderer. |
|
|
|
At the core of the renderer is the array reference COLS (see Example |
|
section below for how to pass this correctly). The COLS array will |
|
consist of entries of one of two types of things: Either an integer |
|
representing one of the pre-packaged column types, or a sub reference |
|
that takes a resource reference, a part number, and a reference to the |
|
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<Apache::lonnavmaps::resource>: |
|
|
|
The general info about the resource: Link, icon for the type, etc. The |
|
first column in the standard nav map display. This column provides the |
|
indentation effect seen in the B<NAV> screen. This column also accepts |
|
the following parameters in the renderer hash: |
|
|
|
=over 4 |
|
|
|
=item * B<resource_nolink>: default false |
|
|
|
If true, the resource will not be linked. By default, all non-folder |
|
resources are linked. |
|
|
|
=item * B<resource_part_count>: default true |
|
|
|
If true, the resource will show a part count B<if> the full |
|
part list is not displayed. (See "condense_parts" later.) If false, |
|
the resource will never show a part count. |
|
|
|
=item * B<resource_no_folder_link>: |
|
|
|
If true, the resource's folder will not be clickable to open or close |
|
it. Default is false. True implies printCloseAll is false, since you |
|
can't close or open folders when this is on anyhow. |
|
|
|
=back |
|
|
|
=item * B<Apache::lonnavmaps::communication_status>: |
|
|
|
Whether there is discussion on the resource, email for the user, or |
|
(lumped in here) perl errors in the execution of the problem. This is |
|
the second column in the main nav map. |
|
|
|
=item * B<Apache::lonnavmaps::quick_status>: |
|
|
|
An icon for the status of a problem, with five possible states: |
|
Correct, incorrect, open, awaiting grading (for a problem where the |
|
computer's grade is suppressed, or the computer can't grade, like |
|
essay problem), or none (not open yet, not a problem). The |
|
third column of the standard navmap. |
|
|
|
=item * B<Apache::lonnavmaps::long_status>: |
|
|
|
A text readout of the details of the current status of the problem, |
|
such as "Due in 22 hours". The fourth column of the standard navmap. |
|
|
|
=item * B<Apache::lonnavmaps::part_status_summary>: |
|
|
|
A text readout summarizing the status of the problem. If it is a |
|
single part problem, will display "Correct", "Incorrect", |
|
"Not yet open", "Open", "Attempted", or "Error". If there are |
|
multiple parts, this will output a string that in HTML will show a |
|
status of how many parts are in each status, in color coding, trying |
|
to match the colors of the icons within reason. |
|
|
|
Note this only makes sense if you are I<not> showing parts. If |
|
C<showParts> is true (see below), this column will not output |
|
anything. |
|
|
|
=back |
|
|
|
If you add any others please be sure to document them here. |
|
|
|
An example of a column renderer that will show the ID number of a |
|
resource, along with the part name if any: |
|
|
|
sub { |
|
my ($resource, $part, $params) = @_; |
|
if ($part) { return '<td>' . $resource->{ID} . ' ' . $part . '</td>'; } |
|
return '<td>' . $resource->{ID} . '</td>'; |
|
} |
|
|
|
Note these functions are responsible for the TD tags, which allow them |
|
to override vertical and horizontal alignment, etc. |
|
|
|
=head2 Parameters |
|
|
|
Minimally, you should be |
|
able to get away with just using 'cols' (to specify the columns |
|
shown), 'url' (necessary for the folders to link to the current screen |
|
correctly), and possibly 'queryString' if your app calls for it. In |
|
that case, maintaining the state of the folders will be done |
|
automatically. |
|
|
|
=over 4 |
|
|
|
=item * B<iterator>: default: constructs one from %env |
|
|
|
A reference to a fresh ::iterator to use from the navmaps. The |
|
rendering will reflect the options passed to the iterator, so you can |
|
use that to just render a certain part of the course, if you like. If |
|
one is not passed, the renderer will attempt to construct one from |
|
env{'form.filter'} and env{'form.condition'} information, plus the |
|
'iterator_map' parameter if any. |
|
|
|
=item * B<iterator_map>: default: not used |
|
|
|
If you are letting the renderer do the iterator handling, you can |
|
instruct the renderer to render only a particular map by passing it |
|
the source of the map you want to process, like |
|
'/res/103/jerf/navmap.course.sequence'. |
|
|
|
=item * B<include_top_level_map>: default: false |
|
|
|
If you need to include the top level map (meaning the course) in the |
|
rendered output set this to true |
|
|
|
=item * B<navmap>: default: constructs one from %env |
|
|
|
A reference to a navmap, used only if an iterator is not passed in. If |
|
this is necessary to make an iterator but it is not passed in, a new |
|
one will be constructed based on env info. This is useful to do basic |
|
error checking before passing it off to render. |
|
|
|
=item * B<r>: default: must be passed in |
|
|
|
The standard Apache response object. This must be passed to the |
|
renderer or the course hash will be locked. |
|
|
|
=item * B<cols>: default: empty (useless) |
|
|
|
An array reference |
|
|
|
=item * B<showParts>:default true |
|
|
|
A flag. If true, a line for the resource itself, and a line |
|
for each part will be displayed. If not, only one line for each |
|
resource will be displayed. |
|
|
|
=item * B<condenseParts>: default true |
|
|
|
A flag. If true, if all parts of the problem have the same |
|
status and that status is Nothing Set, Correct, or Network Failure, |
|
then only one line will be displayed for that resource anyhow. If no, |
|
all parts will always be displayed. If showParts is 0, this is |
|
ignored. |
|
|
|
=item * B<jumpCount>: default: determined from %env |
|
|
|
A string identifying the URL to place the anchor 'curloc' at. |
|
It is the responsibility of the renderer user to |
|
ensure that the #curloc is in the URL. By default, determined through |
|
the use of the env{} 'jump' information, and should normally "just |
|
work" correctly. |
|
|
|
=item * B<here>: default: empty string |
|
|
|
A Symb identifying where to place the 'here' marker. The empty |
|
string means no marker. |
|
|
|
=item * B<indentString>: default: 25 pixel whitespace image |
|
|
|
A string identifying the indentation string to use. |
|
|
|
=item * B<queryString>: default: empty |
|
|
|
A string which will be prepended to the query string used when the |
|
folders are opened or closed. You can use this to pass |
|
application-specific values. |
|
|
|
=item * B<url>: default: none |
|
|
|
The url the folders will link to, which should be the current |
|
page. Required if the resource info column is shown, and you |
|
are allowing the user to open and close folders. |
|
|
|
=item * B<currentJumpIndex>: default: no jumping |
|
|
|
Describes the currently-open row number to cause the browser to jump |
|
to, because the user just opened that folder. By default, pulled from |
|
the Jump information in the env{'form.*'}. |
|
|
|
=item * B<printKey>: default: false |
|
|
|
If true, print the key that appears on the top of the standard |
|
navmaps. |
|
|
|
=item * B<printCloseAll>: default: true |
|
|
|
If true, print the "Close all folders" or "open all folders" |
|
links. |
|
|
|
=item * B<filterFunc>: default: sub {return 1;} (accept everything) |
|
|
|
A function that takes the resource object as its only parameter and |
|
returns a true or false value. If true, the resource is displayed. If |
|
false, it is simply skipped in the display. |
|
|
|
=item * B<suppressEmptySequences>: default: false |
|
|
|
If you're using a filter function, and displaying sequences to orient |
|
the user, then frequently some sequences will be empty. Setting this to |
|
true will cause those sequences not to display, so as not to confuse the |
|
user into thinking that if the sequence is there there should be things |
|
under it; for example, see the "Show Uncompleted Homework" view on the |
|
B<NAV> screen. |
|
|
|
=item * B<suppressNavmaps>: default: false |
|
|
|
If true, will not display Navigate Content resources. |
|
|
|
=back |
|
|
|
=head2 Additional Info |
|
|
|
In addition to the parameters you can pass to the renderer, which will |
|
be passed through unchange to the column renderers, the renderer will |
|
generate the following information which your renderer may find |
|
useful: |
|
|
|
=over 4 |
|
|
|
=item * B<counter>: |
|
|
|
Contains the number of rows printed. Useful after calling the render |
|
function, as you can detect whether anything was printed at all. |
|
|
|
=item * B<isNewBranch>: |
|
|
|
Useful for renderers: If this resource is currently the first resource |
|
of a new branch, this will be true. The Resource column (leftmost in the |
|
navmaps screen) uses this to display the "new branch" icon |
|
|
|
=back |
|
|
|
=cut |
|
|
|
sub resource { return 0; } |
sub resource { return 0; } |
sub communication_status { return 1; } |
sub communication_status { return 1; } |
sub quick_status { return 2; } |
sub quick_status { return 2; } |
Line 761 sub render_resource {
|
Line 882 sub render_resource {
|
my $link = $params->{"resourceLink"}; |
my $link = $params->{"resourceLink"}; |
|
|
# The URL part is not escaped at this point, but the symb is... |
# 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); |
|
$link = $left.'?'.$right; |
|
|
|
my $src = $resource->src(); |
my $src = $resource->src(); |
my $it = $params->{"iterator"}; |
my $it = $params->{"iterator"}; |
Line 778 sub render_resource {
|
Line 894 sub render_resource {
|
my $location=&Apache::loncommon::lonhttpdurl("/adm/lonIcons"); |
my $location=&Apache::loncommon::lonhttpdurl("/adm/lonIcons"); |
# If this is a new branch, label it so |
# If this is a new branch, label it so |
if ($params->{'isNewBranch'}) { |
if ($params->{'isNewBranch'}) { |
$newBranchText = "<img src='$location/branch.gif' border='0' alt='Branch' />"; |
$newBranchText = "<img src='$location/branch.gif' alt=".mt('Branch')." />"; |
} |
} |
|
|
# links to open and close the folder |
# links to open and close the folder |
|
|
|
my $whitespace = $location.'/whitespace_21.gif'; |
my $linkopen = "<a href=\"$link\">"; |
my $linkopen = "<img src='$whitespace' alt='' />"."<a href=\"$link\">"; |
|
|
|
|
my $linkclose = "</a>"; |
my $linkclose = "</a>"; |
|
|
# Default icon: unknown page |
# Default icon: unknown page |
my $icon = "<img src='$location/unknown.gif' alt='' border='0' alt=' ' ' />"; |
my $icon = "<img class=\"LC_contentImage\" src='$location/unknown.gif' alt='' />"; |
|
|
if ($resource->is_problem()) { |
if ($resource->is_problem()) { |
if ($part eq '0' || $params->{'condensed'}) { |
if ($part eq '0' || $params->{'condensed'}) { |
$icon = '<img src="'.$location.'/'; |
$icon = '<img class="LC_contentImage" src="'.$location.'/'; |
if ($resource->is_task()) { |
if ($resource->is_task()) { |
$icon .= 'task.gif" alt="'.&mt('Task'); |
$icon .= 'task.gif" alt="'.&mt('Task'); |
} else { |
} else { |
$icon .= 'problem.gif" alt="'.&mt('Problem'); |
$icon .= 'problem.gif" alt="'.&mt('Problem'); |
} |
} |
$icon .='" border="0" />'; |
$icon .='" />'; |
} else { |
} else { |
$icon = $params->{'indentString'}; |
$icon = $params->{'indentString'}; |
} |
} |
} else { |
} else { |
$icon = "<img src='".&Apache::loncommon::icon($resource->src)."' alt=' ' border='0' />"; |
$icon = "<img class=\"LC_contentImage\" src='".&Apache::loncommon::icon($resource->src)."' alt='' />"; |
} |
} |
|
|
# Display the correct map icon to open or shut map |
# Display the correct map icon to open or shut map |
Line 815 sub render_resource {
|
Line 929 sub render_resource {
|
if ($it->{CONDITION}) { |
if ($it->{CONDITION}) { |
$nowOpen = !$nowOpen; |
$nowOpen = !$nowOpen; |
} |
} |
|
|
my $folderType = $resource->is_sequence() ? 'folder' : 'page'; |
my $folderType = $resource->is_sequence() ? 'folder' : 'page'; |
my $title=$resource->title; |
my $title=$resource->title; |
$title=~s/\"/\"/g; |
$title=~s/\"/\&qout;/g; |
if (!$params->{'resource_no_folder_link'}) { |
if (!$params->{'resource_no_folder_link'}) { |
$icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . '.gif'; |
$icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . '.gif'; |
$icon = "<img src='$location/$icon' alt=\"". |
$icon = "<img src='$location/arrow." . ($nowOpen ? 'closed' : 'open') . ".gif' alt='' />" |
($nowOpen ? &mt('Open Folder') : &mt('Close Folder')).' '.$title."\" border='0' />"; |
."<img class=\"LC_contentImage\" src='$location/$icon' alt=\"" |
|
.($nowOpen ? &mt('Open Folder') : &mt('Close Folder')).' '.$title."\" />"; |
$linkopen = "<a href=\"" . $params->{'url'} . '?' . |
$linkopen = "<a href=\"" . $params->{'url'} . '?' . |
$params->{'queryString'} . '&filter='; |
$params->{'queryString'} . '&filter='; |
$linkopen .= ($nowOpen xor $it->{CONDITION}) ? |
$linkopen .= ($nowOpen xor $it->{CONDITION}) ? |
Line 838 sub render_resource {
|
Line 952 sub render_resource {
|
|
|
} else { |
} else { |
# Don't allow users to manipulate folder |
# Don't allow users to manipulate folder |
$icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . |
$icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . '.gif'; |
'.nomanip.gif'; |
$icon = "<img class=\"LC_space\" src='$whitespace' alt='' />"."<img class=\"LC_contentImage\" src='$location/$icon' alt=\"".($nowOpen ? &mt('Open Folder') : &mt('Close Folder')).' '.$title."\" />"; |
$icon = "<img src='$location/$icon' alt=\"". |
|
($nowOpen ? &mt('Open Folder') : &mt('Close Folder')).' '.$title."\" border='0' />"; |
|
|
|
$linkopen = ""; |
$linkopen = ""; |
$linkclose = ""; |
$linkclose = ""; |
Line 857 sub render_resource {
|
Line 969 sub render_resource {
|
if (($resource->is_practice()) && ($resource->is_raw_problem())) { |
if (($resource->is_practice()) && ($resource->is_raw_problem())) { |
$nonLinkedText .=' <font color="green"><b>'.&mt('not graded').'</b></font>'; |
$nonLinkedText .=' <font color="green"><b>'.&mt('not graded').'</b></font>'; |
} |
} |
|
|
# We're done preparing and finally ready to start the rendering |
|
my $result = "<td align='left' valign='middle'>"; |
|
|
|
|
# We're done preparing and finally ready to start the rendering |
|
my $result = '<td class="LC_middle">'; |
|
my $newfolderType = $resource->is_sequence() ? 'folder' : 'page'; |
|
|
my $indentLevel = $params->{'indentLevel'}; |
my $indentLevel = $params->{'indentLevel'}; |
if ($newBranchText) { $indentLevel--; } |
if ($newBranchText) { $indentLevel--; } |
|
|
Line 870 sub render_resource {
|
Line 983 sub render_resource {
|
} |
} |
|
|
# Decide what to display |
# Decide what to display |
|
|
$result .= "$newBranchText$linkopen$icon$linkclose"; |
$result .= "$newBranchText$linkopen$icon$linkclose"; |
|
|
my $curMarkerBegin = ''; |
my $curMarkerBegin = ''; |
Line 879 sub render_resource {
|
Line 991 sub render_resource {
|
# Is this the current resource? |
# Is this the current resource? |
if (!$params->{'displayedHereMarker'} && |
if (!$params->{'displayedHereMarker'} && |
$resource->symb() eq $params->{'here'} ) { |
$resource->symb() eq $params->{'here'} ) { |
$curMarkerBegin = '<font color="red" size="+2">></font>'; |
$curMarkerBegin = '<em>'; |
$curMarkerEnd = '<font color="red" size="+2"><</font>'; |
$curMarkerEnd = '</em>'; |
$params->{'displayedHereMarker'} = 1; |
$params->{'displayedHereMarker'} = 1; |
} |
} |
|
|
if ($resource->is_problem() && $part ne '0' && |
if ($resource->is_problem() && $part ne '0' && |
Line 901 sub render_resource {
|
Line 1013 sub render_resource {
|
$target=' target="loncapaclient" '; |
$target=' target="loncapaclient" '; |
} |
} |
if (!$params->{'resource_nolink'} && !$resource->is_sequence() && !$resource->is_empty_sequence) { |
if (!$params->{'resource_nolink'} && !$resource->is_sequence() && !$resource->is_empty_sequence) { |
$result .= " $curMarkerBegin<a $target href=\"$link\">$title$partLabel</a>$curMarkerEnd $nonLinkedText</td>"; |
$result .= "$curMarkerBegin<a $target href=\"$link\">$title$partLabel</a>$curMarkerEnd$nonLinkedText</td>"; |
} else { |
} else { |
$result .= " $curMarkerBegin$title$partLabel$curMarkerEnd $nonLinkedText</td>"; |
$result .= "$curMarkerBegin$linkopen$title$partLabel</a>$curMarkerEnd$nonLinkedText</td>"; |
} |
} |
|
|
return $result; |
return $result; |
Line 923 sub render_communication_status {
|
Line 1035 sub render_communication_status {
|
my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc"); |
my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc"); |
if ($resource->hasDiscussion()) { |
if ($resource->hasDiscussion()) { |
$discussionHTML = $linkopen . |
$discussionHTML = $linkopen . |
'<img alt="'.&mt('New Discussion').'" border="0" src="'.$location.'/chat.gif" />' . |
'<img alt="'.&mt('New Discussion').'" src="'.$location.'/chat.gif" />' . |
$linkclose; |
$linkclose; |
} |
} |
|
|
Line 933 sub render_communication_status {
|
Line 1045 sub render_communication_status {
|
if ($msgid) { |
if ($msgid) { |
$feedbackHTML .= ' <a '.$target.' href="/adm/email?display=' |
$feedbackHTML .= ' <a '.$target.' href="/adm/email?display=' |
. &escape($msgid) . '">' |
. &escape($msgid) . '">' |
. '<img alt="'.&mt('New Email').'" src="'.$location.'/feedback.gif" ' |
. '<img alt="'.&mt('New E-mail').'" src="'.$location.'/feedback.gif" /></a>'; |
. 'border="0" /></a>'; |
|
} |
} |
} |
} |
} |
} |
Line 948 sub render_communication_status {
|
Line 1059 sub render_communication_status {
|
$errorcount++; |
$errorcount++; |
$errorHTML .= ' <a '.$target.' href="/adm/email?display=' |
$errorHTML .= ' <a '.$target.' href="/adm/email?display=' |
. &escape($msgid) . '">' |
. &escape($msgid) . '">' |
. '<img alt="'.&mt('New Error').'" src="'.$location.'/bomb.gif" ' |
. '<img alt="'.&mt('New Error').'" src="'.$location.'/bomb.gif" /></a>'; |
. 'border="0" /></a>'; |
|
} |
} |
} |
} |
} |
} |
Line 957 sub render_communication_status {
|
Line 1067 sub render_communication_status {
|
if ($params->{'multipart'} && $part != '0') { |
if ($params->{'multipart'} && $part != '0') { |
$discussionHTML = $feedbackHTML = $errorHTML = ''; |
$discussionHTML = $feedbackHTML = $errorHTML = ''; |
} |
} |
|
return "<td class=\"LC_middle\">$discussionHTML$feedbackHTML$errorHTML </td>"; |
return "<td width=\"75\" align=\"left\" valign=\"middle\">$discussionHTML$feedbackHTML$errorHTML </td>"; |
|
|
|
} |
} |
sub render_quick_status { |
sub render_quick_status { |
Line 974 sub render_quick_status {
|
Line 1083 sub render_quick_status {
|
} |
} |
my $linkopen = "<a $target href=\"$link\">"; |
my $linkopen = "<a $target href=\"$link\">"; |
my $linkclose = "</a>"; |
my $linkclose = "</a>"; |
|
|
|
$result .= '<td class="LC_middle">'; |
if ($resource->is_problem() && |
if ($resource->is_problem() && |
!$firstDisplayed) { |
!$firstDisplayed) { |
|
|
my $icon = $statusIconMap{$resource->simpleStatus($part)}; |
my $icon = $statusIconMap{$resource->simpleStatus($part)}; |
my $alt = $iconAltTags{$icon}; |
my $alt = $iconAltTags{$icon}; |
if ($icon) { |
if ($icon) { |
my $location= |
my $location= |
&Apache::loncommon::lonhttpdurl("/adm/lonIcons/$icon"); |
&Apache::loncommon::lonhttpdurl("/adm/lonIcons/$icon"); |
$result .= "<td valign='middle' width='50' align='right'>$linkopen<img width='25' height='25' src='$location' border='0' alt='$alt' />$linkclose</td>\n"; |
$result .= $linkopen.'<img src="'.$location.'" alt="'.&mt($alt).'" title="'.&mt($alt).'" />'.$linkclose; |
} else { |
} else { |
$result .= "<td width='30'> </td>\n"; |
$result .= " "; |
} |
} |
} else { # not problem, no icon |
} else { # not problem, no icon |
$result .= "<td width='30'> </td>\n"; |
$result .= " "; |
} |
} |
|
$result .= "</td>\n"; |
return $result; |
return $result; |
} |
} |
sub render_long_status { |
sub render_long_status { |
my ($resource, $part, $params) = @_; |
my ($resource, $part, $params) = @_; |
my $result = "<td align='right' valign='middle'>\n"; |
my $result = '<td class="LC_middle LC_right">'; |
my $firstDisplayed = !$params->{'condensed'} && |
my $firstDisplayed = !$params->{'condensed'} && |
$params->{'multipart'} && $part eq "0"; |
$params->{'multipart'} && $part eq "0"; |
|
|
Line 1182 sub render {
|
Line 1291 sub render {
|
if (!defined($navmap)) { |
if (!defined($navmap)) { |
$navmap = Apache::lonnavmaps::navmap->new(); |
$navmap = Apache::lonnavmaps::navmap->new(); |
if (!defined($navmap)) { |
if (!defined($navmap)) { |
# no londer in course |
# no longer in course |
return '<span class="LC_error">'.&mt('No course selected').'</span><br /> |
return '<span class="LC_error">'.&mt('No course selected').'</span><br /> |
<a href="/adm/roles">'.&mt('Select a course').'</a><br />'; |
<a href="/adm/roles">'.&mt('Select a course').'</a><br />'; |
} |
} |
Line 1250 sub render {
|
Line 1359 sub render {
|
# Step 1: Check to see if we have a navmap |
# Step 1: Check to see if we have a navmap |
if (!defined($navmap)) { |
if (!defined($navmap)) { |
$navmap = Apache::lonnavmaps::navmap->new(); |
$navmap = Apache::lonnavmaps::navmap->new(); |
|
if (!defined($navmap)) { |
|
# no longer in course |
|
return '<span class="LC_error">'.&mt('No course selected').'</span><br /> |
|
<a href="/adm/roles">'.&mt('Select a course').'</a><br />'; |
|
} |
} |
} |
|
|
# See if we're being passed a specific map |
# See if we're being passed a specific map |
Line 1294 sub render {
|
Line 1408 sub render {
|
my $printKey = $args->{'printKey'}; |
my $printKey = $args->{'printKey'}; |
my $printCloseAll = $args->{'printCloseAll'}; |
my $printCloseAll = $args->{'printCloseAll'}; |
if (!defined($printCloseAll)) { $printCloseAll = 1; } |
if (!defined($printCloseAll)) { $printCloseAll = 1; } |
|
|
# Print key? |
# Print key? |
if ($printKey) { |
if ($printKey) { |
$result .= '<table border="0" cellpadding="2" cellspacing="0">'; |
$result .= '<table border="0" cellpadding="2" cellspacing="0">'; |
Line 1302 sub render {
|
Line 1416 sub render {
|
my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc"); |
my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc"); |
if ($navmap->{LAST_CHECK}) { |
if ($navmap->{LAST_CHECK}) { |
$result .= |
$result .= |
'<img src="'.$location.'/chat.gif"> '.&mt('New discussion since').' '. |
'<img src="'.$location.'/chat.gif" alt="" /> '.&mt('New discussion since').' '. |
strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})). |
strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})). |
'</td><td align="center" valign="bottom"> '. |
'</td><td align="center" valign="bottom"> '. |
'<img src="'.$location.'/feedback.gif"> '.&mt('New message (click to open)').'<p>'. |
'<img src="'.$location.'/feedback.gif" alt="" /> '.&mt('New message (click to open)').'<p>'. |
'</td>'; |
'</td>'; |
} else { |
} else { |
$result .= '<td align="center" valign="bottom"> '. |
$result .= '<td align="center" valign="bottom"> '. |
'<img src="'.$location.'/chat.gif"> '.&mt('Discussions').'</td><td align="center" valign="bottom">'. |
'<img src="'.$location.'/chat.gif" alt="" /> '.&mt('Discussions').'</td><td align="center" valign="bottom">'. |
' <img src="'.$location.'/feedback.gif"> '.&mt('New message (click to open)'). |
' <img src="'.$location.'/feedback.gif" alt="" /> '.&mt('New message (click to open)'). |
'</td>'; |
'</td>'; |
} |
} |
|
|
Line 1369 END
|
Line 1483 END
|
$result.='</form>'; |
$result.='</form>'; |
} |
} |
|
|
|
|
if ($args->{'caller'} eq 'navmapsdisplay') { |
if ($args->{'caller'} eq 'navmapsdisplay') { |
$result .= '<table><tr><td>'. |
$result .= '<table><tr><td>'. |
&Apache::loncommon::help_open_menu('Navigation Screen','Navigation_Screen',undef,'RAT').'</td>'; |
&Apache::loncommon::help_open_menu('Navigation Screen','Navigation_Screen',undef,'RAT').'</td>'; |
Line 1377 END
|
Line 1492 END
|
} else { |
} else { |
$result .= '</tr><tr>'; |
$result .= '</tr><tr>'; |
} |
} |
$result.=&show_linkitems($args->{'linkitems'}); |
$result.="<td class=\"LC_middle\">".mt('Tools:')."</td>"; |
|
$result.=&show_linkitems_toolbar($args->{'linkitems'}); |
if ($args->{'sort_html'}) { |
if ($args->{'sort_html'}) { |
if ($env{'environment.remotenavmap'} ne 'on') { |
if ($env{'environment.remotenavmap'} ne 'on') { |
$result.='<td> </td><td> </td><td> </td>'. |
$result.='<td> </td><td> </td><td> </td>'. |
Line 1392 END
|
Line 1508 END
|
$result.=$args->{'sort_html'}; |
$result.=$args->{'sort_html'}; |
} |
} |
|
|
$result .= "<br />\n"; |
#$result .= "<br />\n"; |
if ($r) { |
if ($r) { |
$r->print($result); |
$r->print($result); |
$r->rflush(); |
$r->rflush(); |
$result = ""; |
$result = ""; |
} |
} |
# End parameter setting |
# End parameter setting |
|
|
|
$result .= "<br />\n"; |
|
|
# Data |
# Data |
$result .= '<table cellspacing="0" cellpadding="3" border="0" bgcolor="#FFFFFF">' ."\n"; |
$result.=&Apache::loncommon::start_data_table("LC_tableOfContent"); |
|
|
my $res = "Apache::lonnavmaps::resource"; |
my $res = "Apache::lonnavmaps::resource"; |
my %condenseStatuses = |
my %condenseStatuses = |
( $res->NETWORK_FAILURE => 1, |
( $res->NETWORK_FAILURE => 1, |
$res->NOTHING_SET => 1, |
$res->NOTHING_SET => 1, |
$res->CORRECT => 1 ); |
$res->CORRECT => 1 ); |
my @backgroundColors = ("#FFFFFF", "#F6F6F6"); |
my @backgroundColors = ("LC_trEven", "LC_trOdd"); |
|
|
# Shared variables |
# Shared variables |
$args->{'counter'} = 0; # counts the rows |
$args->{'counter'} = 0; # counts the rows |
$args->{'indentLevel'} = 0; |
$args->{'indentLevel'} = 0; |
$args->{'isNewBranch'} = 0; |
$args->{'isNewBranch'} = 0; |
$args->{'condensed'} = 0; |
$args->{'condensed'} = 0; |
my $location= |
|
&Apache::loncommon::lonhttpdurl("/adm/lonIcons/whitespace1.gif"); |
my $location = &Apache::loncommon::lonhttpdurl("/adm/lonIcons/whitespace_21.gif"); |
$args->{'indentString'} = setDefault($args->{'indentString'}, "<img src='$location' width='25' height='1' alt=' ' border='0' />"); |
$args->{'indentString'} = setDefault($args->{'indentString'}, "<img src='$location' alt='' />"); |
$args->{'displayedHereMarker'} = 0; |
$args->{'displayedHereMarker'} = 0; |
|
|
# If we're suppressing empty sequences, look for them here. Use DFS for speed, |
# If we're suppressing empty sequences, look for them here. Use DFS for speed, |
Line 1644 END
|
Line 1763 END
|
$rownum ++; |
$rownum ++; |
my $backgroundColor = $backgroundColors[$rownum % scalar(@backgroundColors)]; |
my $backgroundColor = $backgroundColors[$rownum % scalar(@backgroundColors)]; |
|
|
$result .= " <tr bgcolor='$backgroundColor'>\n"; |
$result .= &Apache::loncommon::start_data_table_row(); |
|
|
# Set up some data about the parts that the cols might want |
# Set up some data about the parts that the cols might want |
my $filter = $it->{FILTER}; |
my $filter = $it->{FILTER}; |
Line 1670 END
|
Line 1789 END
|
} |
} |
$result .= $colHTML . "\n"; |
$result .= $colHTML . "\n"; |
} |
} |
$result .= " </tr>\n"; |
$result .= &Apache::loncommon::end_data_table_row(); |
$args->{'isNewBranch'} = 0; |
$args->{'isNewBranch'} = 0; |
} |
} |
|
|
Line 1706 if (location.href.indexOf('#curloc')==-1
|
Line 1825 if (location.href.indexOf('#curloc')==-1
|
</script>"; |
</script>"; |
} |
} |
|
|
$result .= "</table>"; |
$result.=&Apache::loncommon::end_data_table(); |
|
|
if ($r) { |
if ($r) { |
$r->print($result); |
$r->print($result); |
$result = ""; |
$result = ""; |
Line 1757 ENDBLOCK
|
Line 1876 ENDBLOCK
|
return $result; |
return $result; |
} |
} |
|
|
|
sub show_linkitems_toolbar { |
|
my ($linkitems,$condition)=@_; |
|
my @linkorder = ("blank","launchnav","closenav","firsthomework", |
|
"everything","uncompleted","changefolder","clearbubbles"); |
|
|
|
my $result .=' |
|
<td align="left"> |
|
<span class="LC_nobreak">'."\n<ul id=\"LC_toolbar\">"; |
|
foreach my $link (@linkorder) { |
|
my $link_id = "LC_content_toolbar_".$link; |
|
if (defined($linkitems->{$link})) { |
|
if ($linkitems->{$link}{'text'} ne '') { |
|
$linkitems->{$link}{'cmd'}=~s/"/'/g; |
|
if($linkitems->{$link}{'cmd'}){ |
|
if($link eq 'changefolder'){ |
|
if($condition){$link_id='LC_content_toolbar_changefolder_toggled'} |
|
else{$link_id='LC_content_toolbar_changefolder'} |
|
} |
|
$result .= ' <li><a href="#"'. |
|
' onClick="'.$linkitems->{$link}{'cmd'}.'"'. |
|
' id="'.$link_id.'"'. |
|
' class="LC_toolbarItem"'. |
|
' title="'.$linkitems->{$link}{'text'}.'"></a></li>'."\n"; |
|
} |
|
|
|
} |
|
} |
|
} |
|
$result .= '</ul>'; |
|
$result .= ' </span></td>'."\n"; |
|
|
|
return $result; |
|
} |
|
|
|
|
1; |
1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
package Apache::lonnavmaps::navmap; |
package Apache::lonnavmaps::navmap; |
|
|
=pod |
=pod |
Line 3642 sub map_contains_problem {
|
Line 3804 sub map_contains_problem {
|
sub is_sequence { |
sub is_sequence { |
my $self=shift; |
my $self=shift; |
return $self->navHash("is_map_", 1) && |
return $self->navHash("is_map_", 1) && |
$self->navHash("map_type_" . $self->map_pc()) eq 'sequence'; |
$self->navHash("map_type_" . $self->map_pc()) eq 'sequence'; |
} |
} |
sub is_survey { |
sub is_survey { |
my $self = shift(); |
my $self = shift(); |
Line 3844 sub awarded {
|
Line 4006 sub awarded {
|
if (!defined($part)) { $part = '0'; } |
if (!defined($part)) { $part = '0'; } |
return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.awarded'}; |
return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.awarded'}; |
} |
} |
|
sub taskversion { |
|
my $self = shift; my $part = shift; |
|
$self->{NAV_MAP}->get_user_data(); |
|
if (!defined($part)) { $part = '0'; } |
|
return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.version'}; |
|
} |
|
sub taskstatus { |
|
my $self = shift; my $part = shift; |
|
$self->{NAV_MAP}->get_user_data(); |
|
if (!defined($part)) { $part = '0'; } |
|
return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$self->taskversion($part).'.'.$part.'.status'}; |
|
} |
|
sub solved { |
|
my $self = shift; my $part = shift; |
|
$self->{NAV_MAP}->get_user_data(); |
|
if (!defined($part)) { $part = '0'; } |
|
return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.solved'}; |
|
} |
|
sub checkedin { |
|
my $self = shift; my $part = shift; |
|
$self->{NAV_MAP}->get_user_data(); |
|
if (!defined($part)) { $part = '0'; } |
|
if ($self->is_task()) { |
|
my $version = $self->taskversion($part); |
|
return ($self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$version .'.'.$part.'.checkedin'},$self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$version .'.'.$part.'.checkedin.slot'}); |
|
} else { |
|
return ($self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.checkedin'},$self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.checkedin.slot'}); |
|
} |
|
} |
# this should work exactly like the copy in lonhomework.pm |
# this should work exactly like the copy in lonhomework.pm |
sub duedate { |
sub duedate { |
(my $self, my $part) = @_; |
(my $self, my $part) = @_; |
Line 3939 sub part_display {
|
Line 4130 sub part_display {
|
} |
} |
return $display; |
return $display; |
} |
} |
|
sub slot_control { |
|
my $self=shift(); my $part = shift(); |
|
if (!defined($part)) { $part = '0'; } |
|
my $useslots = $self->parmval("useslots", $part); |
|
my $availablestudent = $self->parmval("availablestudent", $part); |
|
my $available = $self->parmval("available", $part); |
|
return ($useslots,$availablestudent,$available); |
|
} |
|
|
# Multiple things need this |
# Multiple things need this |
sub getReturnHash { |
sub getReturnHash { |
Line 4371 sub OPEN { return 1; }
|
Line 4570 sub OPEN { return 1; }
|
sub PAST_DUE_NO_ANSWER { return 2; } |
sub PAST_DUE_NO_ANSWER { return 2; } |
sub PAST_DUE_ANSWER_LATER { return 3; } |
sub PAST_DUE_ANSWER_LATER { return 3; } |
sub ANSWER_OPEN { return 4; } |
sub ANSWER_OPEN { return 4; } |
sub NOTHING_SET { return 5; } |
sub NOTHING_SET { return 5; } |
sub NETWORK_FAILURE { return 100; } |
sub NETWORK_FAILURE { return 100; } |
|
|
# getDateStatus gets the date status for a given problem part. |
# getDateStatus gets the date status for a given problem part. |
Line 4583 An answer has been submitted, but the st
|
Line 4782 An answer has been submitted, but the st
|
|
|
=cut |
=cut |
|
|
sub TRIES_LEFT { return 20; } |
sub TRIES_LEFT { return 20; } |
sub ANSWER_SUBMITTED { return 21; } |
sub ANSWER_SUBMITTED { return 21; } |
sub PARTIALLY_CORRECT{ return 22; } |
sub PARTIALLY_CORRECT { return 22; } |
|
|
|
sub RESERVED_LATER { return 30; } |
|
sub RESERVED { return 31; } |
|
sub RESERVED_LOCATION { return 32; } |
|
sub RESERVABLE { return 33; } |
|
sub RESERVABLE_LATER { return 34; } |
|
sub NOTRESERVABLE { return 35; } |
|
sub NOT_IN_A_SLOT { return 36; } |
|
sub NEEDS_CHECKIN { return 37; } |
|
sub WAITING_FOR_GRADE { return 38; } |
|
sub UNKNOWN { return 39; } |
|
|
sub status { |
sub status { |
my $self = shift; |
my $self = shift; |
Line 4678 sub status {
|
Line 4888 sub status {
|
return OPEN; |
return OPEN; |
} |
} |
|
|
|
sub check_for_slot { |
|
my $self = shift; |
|
my $part = shift; |
|
my ($use_slots,$available,$availablestudent) = $self->slot_control($part); |
|
if (($use_slots ne '') && ($use_slots !~ /^\s*no\s*$/i)) { |
|
my @slots = (split(/:/,$availablestudent),split(/:/,$available)); |
|
my $cid=$env{'request.course.id'}; |
|
my $cdom=$env{'course.'.$cid.'.domain'}; |
|
my $cnum=$env{'course.'.$cid.'.num'}; |
|
my $now = time; |
|
if (@slots > 0) { |
|
my %slots=&Apache::lonnet::get('slots',[@slots],$cdom,$cnum); |
|
if (&Apache::lonnet::error(%slots)) { |
|
return (UNKNOWN); |
|
} |
|
my @sorted_slots = &Apache::loncommon::sorted_slots(\@slots,\%slots); |
|
my ($checkedin,$checkedinslot); |
|
foreach my $slot_name (@sorted_slots) { |
|
next if (!defined($slots{$slot_name}) || |
|
!ref($slots{$slot_name})); |
|
my $end = $slots{$slot_name}->{'endtime'}; |
|
my $start = $slots{$slot_name}->{'starttime'}; |
|
my $ip = $slots{$slot_name}->{'ip'}; |
|
if ($self->simpleStatus() == OPEN) { |
|
my $startreserve = $slots{$slot_name}->{'startreserve'}; |
|
my @proctors; |
|
if ($slots{$slot_name}->{'proctor'} ne '') { |
|
@proctors = split(',',$slots{$slot_name}->{'proctor'}); |
|
} |
|
if ($end > $now) { |
|
($checkedin,$checkedinslot) = $self->checkedin(); |
|
if ($startreserve < $now) { |
|
if ($start > $now) { |
|
return (RESERVED_LATER,$start,$slot_name); |
|
} else { |
|
if ($ip ne '') { |
|
if (!&Apache::loncommon::check_ip_acc($ip)) { |
|
return (RESERVED_LOCATION,$ip,$slot_name); |
|
} |
|
} |
|
if (@proctors > 0) { |
|
unless ((grep(/^\Q$checkedin\E/,@proctors)) && |
|
($checkedinslot eq $slot_name)) { |
|
return (NEEDS_CHECKIN,undef,$slot_name); |
|
} |
|
} |
|
return (RESERVED,$end,$slot_name); |
|
} |
|
} else { |
|
if ($start > $now) { |
|
return (RESERVABLE,$startreserve,$slot_name); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
my ($is_correct,$got_grade); |
|
if ($self->is_task()) { |
|
my $taskstatus = $self->taskstatus(); |
|
$is_correct = (($taskstatus eq 'pass') || |
|
($self->solved() =~ /^correct_/)); |
|
$got_grade = ($self->solved() =~ /^(?:pass|fail)$/); |
|
} else { |
|
$got_grade = 1; |
|
$is_correct = ($self->solved() =~ /^correct_/); |
|
} |
|
($checkedin,$checkedinslot) = $self->checkedin(); |
|
if ($checkedin) { |
|
if (!$got_grade) { |
|
return (WAITING_FOR_GRADE); |
|
} elsif ($is_correct) { |
|
return (CORRECT); |
|
} |
|
} |
|
return(NOT_IN_A_SLOT); |
|
} else { |
|
if (!$future_slots_checked) { |
|
$future_slots = &get_future_slots($cdom,$cnum,$now); |
|
$future_slots_checked = 1; |
|
} |
|
if ($future_slots) { |
|
return(NOT_IN_A_SLOT); |
|
} |
|
return(NOTRESERVABLE); |
|
} |
|
} |
|
return; |
|
} |
|
|
|
sub get_future_slots { |
|
my ($cdom,$cnum,$now) = @_; |
|
my %slots=&Apache::lonnet::dump('slots',$cdom,$cnum); |
|
my $future_slots = 0; |
|
foreach my $slot (keys(%slots)) { |
|
if (($slots{$slot}->{'starttime'} > $now) && |
|
($slots{$slot}->{'endtime'} > $now)) { |
|
$future_slots ++; |
|
} |
|
} |
|
return $future_slots; |
|
} |
|
|
sub CLOSED { return 23; } |
sub CLOSED { return 23; } |
sub ERROR { return 24; } |
sub ERROR { return 24; } |
|
|