version 1.54, 2002/09/24 20:01:05
|
version 1.65, 2002/10/04 15:38:22
|
Line 124 sub parmval {
|
Line 124 sub parmval {
|
my $courselevelm=$usercourseprefix.'.'.$mapparm; |
my $courselevelm=$usercourseprefix.'.'.$mapparm; |
|
|
# ---------------------------------------------------------- first, check user |
# ---------------------------------------------------------- first, check user |
if ($uname) { |
if (defined($uname)) { |
if ($useropt{$courselevelr}) { return $useropt{$courselevelr}; } |
if (defined($useropt{$courselevelr})) { return $useropt{$courselevelr}; } |
if ($useropt{$courselevelm}) { return $useropt{$courselevelm}; } |
if (defined($useropt{$courselevelm})) { return $useropt{$courselevelm}; } |
if ($useropt{$courselevel}) { return $useropt{$courselevel}; } |
if (defined($useropt{$courselevel})) { return $useropt{$courselevel}; } |
} |
} |
|
|
# ------------------------------------------------------- second, check course |
# ------------------------------------------------------- second, check course |
if ($csec) { |
if (defined($csec)) { |
if ($courseopt{$seclevelr}) { return $courseopt{$seclevelr}; } |
if (defined($courseopt{$seclevelr})) { return $courseopt{$seclevelr}; } |
if ($courseopt{$seclevelm}) { return $courseopt{$seclevelm}; } |
if (defined($courseopt{$seclevelm})) { return $courseopt{$seclevelm}; } |
if ($courseopt{$seclevel}) { return $courseopt{$seclevel}; } |
if (defined($courseopt{$seclevel})) { return $courseopt{$seclevel}; } |
} |
} |
|
|
if ($courseopt{$courselevelr}) { return $courseopt{$courselevelr}; } |
if (defined($courseopt{$courselevelr})) { return $courseopt{$courselevelr}; } |
if ($courseopt{$courselevelm}) { return $courseopt{$courselevelm}; } |
if (defined($courseopt{$courselevelm})) { return $courseopt{$courselevelm}; } |
if ($courseopt{$courselevel}) { return $courseopt{$courselevel}; } |
if (defined($courseopt{$courselevel})) { return $courseopt{$courselevel}; } |
|
|
# ----------------------------------------------------- third, check map parms |
# ----------------------------------------------------- third, check map parms |
|
|
my $thisparm=$parmhash{$symbparm}; |
my $thisparm=$parmhash{$symbparm}; |
if ($thisparm) { return $thisparm; } |
if (defined($thisparm)) { return $thisparm; } |
|
|
# ----------------------------------------------------- fourth , check default |
# ----------------------------------------------------- fourth , check default |
|
|
my $default=&Apache::lonnet::metadata($fn,$rwhat.'.default'); |
my $default=&Apache::lonnet::metadata($fn,$rwhat.'.default'); |
if ($default) { return $default} |
if (defined($default)) { return $default} |
|
|
# --------------------------------------------------- fifth , cascade up parts |
# --------------------------------------------------- fifth , cascade up parts |
|
|
Line 159 sub parmval {
|
Line 159 sub parmval {
|
my ($part,$id)=split(/\_/,$space); |
my ($part,$id)=split(/\_/,$space); |
if ($id) { |
if ($id) { |
my $partgeneral=&parmval($part.".$qualifier",$symb); |
my $partgeneral=&parmval($part.".$qualifier",$symb); |
if ($partgeneral) { return $partgeneral; } |
if (defined($partgeneral)) { return $partgeneral; } |
} else { |
} else { |
my $resourcegeneral=&parmval("0.$qualifier",$symb); |
my $resourcegeneral=&parmval("0.$qualifier",$symb); |
if ($resourcegeneral) { return $resourcegeneral; } |
if (defined($resourcegeneral)) { return $resourcegeneral; } |
} |
} |
} |
} |
return ''; |
return ''; |
Line 819 sub new_handle {
|
Line 819 sub new_handle {
|
&Apache::loncommon::no_cache($r); |
&Apache::loncommon::no_cache($r); |
$r->send_http_header; |
$r->send_http_header; |
|
|
# Header |
|
$r->print(&Apache::loncommon::bodytag('Navigate Course Map','', |
|
'')); |
|
# Initialize the nav map |
# Initialize the nav map |
my $navmap = Apache::lonnavmaps::navmap->new( |
my $navmap = Apache::lonnavmaps::navmap->new( |
$ENV{"request.course.fn"}.".db", |
$ENV{"request.course.fn"}.".db", |
$ENV{"request.course.fn"}."_parms.db", 1); |
$ENV{"request.course.fn"}."_parms.db", 1); |
|
|
|
|
|
if (!defined($navmap)) { |
|
my $requrl = $r->uri; |
|
$ENV{'user.error.msg'} = "$requrl:bre:0:0:Course not initialized"; |
|
return HTTP_NOT_ACCEPTABLE; |
|
} |
|
|
|
# Header |
|
$r->print(&Apache::loncommon::bodytag('Navigate Course Map','', |
|
'')); |
|
$r->print('<script>window.focus();</script>'); |
|
my $desc=$ENV{'course.'.$ENV{'request.course.id'}.'.description'}; |
|
if (defined($desc)) { $r->print("<h2>$desc</h2>\n"); } |
|
my $date=localtime; |
|
$r->print("<h3>$date</h3>\n"); |
|
$r->rflush(); |
|
if ($navmap->{LAST_CHECK}) { |
|
$r->print('<img src="/adm/lonMisc/chat.gif"> New discussion since '. |
|
timeToHumanString($navmap->{LAST_CHECK}). |
|
'<br><img src="/adm/lonMisc/feedback.gif"> New message (click to open)<p>'); |
|
} else { |
|
$r->print('<img src="/adm/lonMisc/chat.gif"> Discussions'. |
|
'<br><img src="/adm/lonMisc/feedback.gif"> New message (click to open)<p>'); |
|
} |
|
#if (($currenturl=~/^\/res/) && |
|
# ($currenturl!~/^\/res\/adm/)) { |
|
# $r->print('<a href="#curloc">Current Location</a><p>'); |
|
#} |
|
|
|
# Check that it's defined |
|
if (!($navmap->courseMapDefined())) { |
|
$r->print('<font size="+2" color="red">Coursemap undefined.</font>' . |
|
'</body></html>'); |
|
return OK; |
|
} |
|
|
# Grab a resource object so we have access to the constants; this |
# Grab a resource object so we have access to the constants; this |
# is technically not proper, but should be harmless |
# is technically not proper, but should be harmless |
my $res = $navmap->firstResource(); |
my $res = $navmap->firstResource(); |
Line 834 sub new_handle {
|
Line 867 sub new_handle {
|
# 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 = |
( $res->NETWORK_FAILURE => '', |
( $res->NETWORK_FAILURE => '', |
$res->CORRECT => '#BBFFBB', |
$res->CORRECT => '', |
$res->EXCUSED => '#BBBBFF', |
$res->EXCUSED => '#BBBBFF', |
$res->PAST_DUE_ANSWER_LATER => '#FFAA00', |
$res->PAST_DUE_ANSWER_LATER => '', |
$res->PAST_DUE_NO_ANSWER => '#FFAA00', |
$res->PAST_DUE_NO_ANSWER => '', |
$res->ANSWER_OPEN => '#FF00AA', |
$res->ANSWER_OPEN => '#CCFFCC', |
$res->OPEN_LATER => '', |
$res->OPEN_LATER => '', |
$res->TRIES_LEFT => '#FFFF00', |
$res->TRIES_LEFT => '', |
$res->INCORRECT => '#FFAA00', |
$res->INCORRECT => '', |
$res->OPEN => '#FFFF88', |
$res->OPEN => '', |
$res->NOTHING_SET => '' ); |
$res->NOTHING_SET => '' ); |
|
# And a special case in the nav map; what to do when the assignment |
if (!defined($navmap)) { |
# is not yet done and due in less then 24 hours |
my $requrl = $r->uri; |
my $hurryUpColor = "#FFCCCC"; |
$ENV{'user.error.msg'} = "$requrl:bre:0:0:Course not initialized"; |
|
return HTTP_NOT_ACCEPTABLE; |
my %statusIconMap = |
} |
( $res->NETWORK_FAILURE => '', |
|
$res->NOTHING_SET => '', |
|
$res->CORRECT => 'navmap.correct.gif', |
|
$res->EXCUSED => 'navmap.correct.gif', |
|
$res->PAST_DUE_NO_ANSWER => 'navmap.wrong.gif', |
|
$res->PAST_DUE_ANSWER_LATER => 'navmap.wrong.gif', |
|
$res->ANSWER_OPEN => 'navmap.wrong.gif', |
|
$res->OPEN_LATER => '', |
|
$res->TRIES_LEFT => 'navmap.open.gif', |
|
$res->INCORRECT => 'navmap.wrong.gif', |
|
$res->OPEN => 'navmap.open.gif' ); |
|
|
|
my %condenseStatuses = |
|
( $res->NETWORK_FAILURE => 1, |
|
$res->NOTHING_SET => 1, |
|
$res->CORRECT => 1 ); |
|
|
my %filterHash; |
my %filterHash; |
# Figure out what we're not displaying |
# Figure out what we're not displaying |
Line 859 sub new_handle {
|
Line 907 sub new_handle {
|
} |
} |
} |
} |
|
|
|
my $currenturl = $ENV{'form.postdata'}; |
|
$currenturl=~s/^http\:\/\///; |
|
$currenturl=~s/^[^\/]+//; |
|
my $queryAdd = "postdata=" . &Apache::lonnet::escape($currenturl); |
|
|
|
$r->print("$currenturl<br/>"); |
|
|
|
|
# Begin the HTML table |
# Begin the HTML table |
# two columns: the title/icon on the left, the info on the right |
# four cols: resource + indent, chat+feedback, icon, text string |
$r->print('<table cellspacing="3" cellpadding="0" bgcolor="#FFFFFF">' ."\n"); |
$r->print('<table cellspacing="3" cellpadding="0" bgcolor="#FFFFFF">' ."\n"); |
|
|
my $mapIterator = $navmap->getIterator(undef, undef, \%filterHash); |
my $mapIterator = $navmap->getIterator(undef, undef, \%filterHash, 0); |
my $curRes = $mapIterator->next(); |
my $curRes = $mapIterator->next(); |
undef $res; # so we don't accidentally use it later |
undef $res; # so we don't accidentally use it later |
my $indentLevel = -1; |
my $indentLevel = -1; |
my $indentString = " "; |
my $indentString = "<img src=\"/adm/lonIcons/whitespace1.gif\" width=\"25\" height=\"1\" alt=\"\" border=\"0\" />"; |
|
|
my $isNewBranch = 0; |
my $isNewBranch = 0; |
|
my $now = time(); |
|
my $in24Hours = $now + 24 * 60 * 60; |
|
|
while ($curRes != $mapIterator->END_NAV_MAP) { |
while ($curRes != $mapIterator->END_NAV_MAP) { |
if ($curRes == $mapIterator->BEGIN_MAP() || |
if ($curRes == $mapIterator->BEGIN_MAP() || |
Line 886 sub new_handle {
|
Line 944 sub new_handle {
|
|
|
if (ref($curRes) && $curRes->src()) { |
if (ref($curRes) && $curRes->src()) { |
|
|
my @parts; |
# Step one: Decide which parts to show |
|
my @parts = @{$curRes->parts()}; |
|
my $multipart = scalar(@parts) > 1; |
|
my $condensed = 0; |
|
|
if ($curRes->is_problem()) { |
if ($curRes->is_problem()) { |
@parts = @{$curRes->parts()}; |
# Is it multipart? |
if (scalar(@parts) > 1) { shift(@parts); } |
|
|
if ($multipart) { |
|
# If it's multipart, see if part 0 is "open" |
|
# if it is, display all parts, if it isn't, |
|
# just display first |
|
if (!$curRes->opendate("0")) { |
|
@parts = ("0"); # just display the zero-th part |
|
} else { |
|
# Otherwise, only display part 0 if we want to |
|
# attach feedback or email information to it |
|
if ($curRes->hasDiscussion() || $curRes->getFeedback()) { |
|
shift @parts; |
|
} else { |
|
# If there's discussion or feedback, that counts |
|
# as a difference, so display the parts. |
|
|
|
# Now, we decide whether to condense the |
|
# parts due to similarity |
|
my $status = $curRes->status($parts[1]); |
|
my $due = $curRes->duedate($parts[1]); |
|
my $open = $curRes->opendate($parts[1]); |
|
my $statusAllSame = 1; |
|
my $dueAllSame = 1; |
|
my $openAllSame = 1; |
|
for (my $i = 2; $i < scalar(@parts); $i++) { |
|
if ($curRes->status($parts[$i]) != $status){ |
|
$statusAllSame = 0; |
|
} |
|
if ($curRes->duedate($parts[$i]) != $due ) { |
|
$dueAllSame = 0; |
|
} |
|
if ($curRes->opendate($parts[$i]) != $open) { |
|
$openAllSame = 0; |
|
} |
|
} |
|
|
|
# $allSame is true if all the statuses were |
|
# the same. Now, if they are all the same and |
|
# match one of the statuses to condense, or they |
|
# are all open with the same due date, or they are |
|
# all OPEN_LATER with the same open date, display the |
|
# status of the first non-zero part (to get the 'correct' |
|
# status right, since 0 is never 'correct' or 'open'). |
|
if (($statusAllSame && defined($condenseStatuses{$status})) || |
|
($dueAllSame && $status == $curRes->OPEN && $statusAllSame)|| |
|
($openAllSame && $status == $curRes->OPEN_LATER && $statusAllSame) ){ |
|
@parts = ($parts[1]); |
|
$condensed = 1; |
|
} |
|
} |
|
} |
|
} |
|
|
} else { |
} else { |
@parts[0] = "0"; # this is to get past foreach loop below |
@parts[0] = "0"; # this is to get past foreach loop below |
# you can consider a non-problem resource as a resource |
# you can consider a non-problem resource as a resource |
# with only one part without loss |
# with only one part without loss |
} |
} |
|
|
|
# Step two: Show the parts |
foreach my $part (@parts) { |
foreach my $part (@parts) { |
|
|
|
my $deltalevel = 0; # for inserting the branch icon |
|
|
# For each thing we're displaying... |
# For each thing we're displaying... |
|
|
Line 915 sub new_handle {
|
Line 1032 sub new_handle {
|
# If this is a new branch, label it so |
# If this is a new branch, label it so |
# (temporary, this should be an icon w/ alt text) |
# (temporary, this should be an icon w/ alt text) |
if ($isNewBranch) { |
if ($isNewBranch) { |
$newBranchText = "NB -> "; |
$newBranchText = "<img src=\"/adm/lonIcons/branch.gif\" border=\"0\">"; |
$isNewBranch = 0; |
$isNewBranch = 0; |
|
$deltalevel = 1; |
} |
} |
|
|
# links to open and close the folders |
# links to open and close the folders |
my $linkopen = "<a href=\"$link\">"; |
my $linkopen = "<a href=\"$link\">"; |
my $linkclose = "</a>"; |
my $linkclose = "</a>"; |
|
|
my $icon = "html.gif"; |
my $icon = "<img src=\"/adm/lonIcons/html.gif\" alt=\"\" border=\"0\" />"; |
if ($curRes->is_problem()) { |
if ($curRes->is_problem()) { |
$icon = "problem.gif"; |
if ($part eq "0" || $condensed) { |
|
$icon = '<img src="/adm/lonIcons/problem.gif" alt="" border=\"0\" />'; |
|
} else { |
|
$icon = $indentString; |
|
} |
} |
} |
|
|
# Display the correct icon, link to open or shut map |
# Display the correct icon, link to open or shut map |
Line 933 sub new_handle {
|
Line 1055 sub new_handle {
|
my $mapId = $curRes->map_pc(); |
my $mapId = $curRes->map_pc(); |
my $nowOpen = !defined($filterHash{$mapId}); |
my $nowOpen = !defined($filterHash{$mapId}); |
$icon = $nowOpen ? |
$icon = $nowOpen ? |
"folder_opened.gif" : "folder_closed.gif"; |
"folder_closed.gif" : "folder_opened.gif"; |
|
$icon = "<img src=\"/adm/lonIcons/$icon\" alt=\"\" border=\"0\" />"; |
$linkopen = "<a href=\"/adm/navmaps?filter="; |
$linkopen = "<a href=\"/adm/navmaps?filter="; |
$linkopen .= $nowOpen ? |
$linkopen .= $nowOpen ? |
addToFilter(\%filterHash, $mapId) : |
addToFilter(\%filterHash, $mapId) : |
removeFromFilter(\%filterHash, $mapId); |
removeFromFilter(\%filterHash, $mapId); |
$linkopen .= "\">"; |
$linkopen .= "&$queryAdd\">"; |
$linkclose = "</a>"; |
$linkclose = "</a>"; |
|
|
} |
} |
Line 952 sub new_handle {
|
Line 1075 sub new_handle {
|
} |
} |
} |
} |
|
|
|
# FIRST ROW: The resource indentation, branch icon, and name |
$r->print(" <tr><td align=\"left\" valign=\"top\">\n"); |
$r->print(" <tr><td align=\"left\" valign=\"top\">\n"); |
|
|
# print indentation |
# print indentation |
for (my $i = 0; $i < $indentLevel; $i++) { |
for (my $i = 0; $i < $indentLevel - $deltalevel; $i++) { |
$r->print($indentString); |
$r->print($indentString); |
} |
} |
|
|
$r->print(" ${newBranchText}${linkopen}<img border=\"0\" src=\"" . |
$r->print(" ${newBranchText}${linkopen}$icon${linkclose}\n"); |
"/adm/lonIcons/$icon\" alt=\"\">${linkclose}\n"); |
|
|
|
if ($curRes->is_problem() && $part != "0") { $partLabel = " (Part $part)"; } |
if ($curRes->is_problem() && $part != "0" && !$condensed) { |
|
$partLabel = " (Part $part)"; |
|
$title = ""; |
|
} |
|
|
|
$r->print(" <a href=\"$link\">$title$partLabel</a></td>"); |
|
|
|
my $discussionHTML = ""; my $feedbackHTML = ""; |
|
|
|
# SECOND ROW: Is there text or feedback? |
|
if ($curRes->hasDiscussion()) { |
|
$discussionHTML = $linkopen . |
|
'<img border="0" src="/adm/lonMisc/chat.gif" />' . |
|
$linkclose; |
|
} |
|
|
|
if ($curRes->getFeedback()) { |
|
my $feedback = $curRes->getFeedback(); |
|
foreach (split(/\,/, $feedback)) { |
|
if ($_) { |
|
$feedbackHTML .= ' <a href="/adm/email?display=' |
|
. &Apache::lonnet::escape($_) . '">' |
|
. '<img src="/adm/lonMisc/feedback.gif" ' |
|
. 'border="0" /></a>'; |
|
} |
|
} |
|
} |
|
|
|
$r->print("<td align=\"left\">$discussionHTML$feedbackHTML</td>"); |
|
|
|
# Is this the first displayed part of a multi-part problem |
|
# that has not been condensed, so we should suppress these two |
|
# columns? |
|
my $firstDisplayed = !$condensed && $multipart && $part eq "0"; |
|
|
|
# THIRD ROW: Problem status icon |
|
if ($curRes->is_problem() && |
|
!$firstDisplayed) { |
|
my $icon = $statusIconMap{$curRes->status($part)}; |
|
if ($icon) { |
|
$r->print("<td width=\"50\" align=\"right\">$linkopen<img src=\"/adm/lonIcons/$icon\" border=\"0\" />$linkclose</td>\n"); |
|
} else { |
|
$r->print("<td></td>\n"); |
|
} |
|
} else { # not problem, no icon |
|
$r->print("<td></td>\n"); |
|
} |
|
|
$r->print(" <a href=\"$link\">$title$partLabel</a></td>". |
# FOURTH ROW: Text description |
"<td $colorizer align=\"right\">\n"); |
$r->print("<td $colorizer align=\"right\">\n"); |
|
|
if ($curRes->kind() eq "res" and |
if ($curRes->kind() eq "res" && |
$curRes->is_problem() ) { |
$curRes->is_problem() && |
|
!$firstDisplayed) { |
$r->print (getDescription($curRes, $part)); |
$r->print (getDescription($curRes, $part)); |
} |
} |
|
|
|
$r->print("</td></tr>\n"); |
} |
} |
} |
} |
$curRes = $mapIterator->next(); |
$curRes = $mapIterator->next(); |
Line 978 sub new_handle {
|
Line 1150 sub new_handle {
|
|
|
$r->print("</table></body></html>"); |
$r->print("</table></body></html>"); |
|
|
|
$navmap->untieHashes(); |
|
|
return OK; |
return OK; |
} |
} |
|
|
Line 1046 sub getDescription {
|
Line 1220 sub getDescription {
|
return "Opens: " . timeToHumanString($res->opendate($part)); |
return "Opens: " . timeToHumanString($res->opendate($part)); |
} |
} |
if ($status == $res->OPEN) { |
if ($status == $res->OPEN) { |
return "Due: $status " . timeToHumanString($res->duedate($part)); |
if ($res->duedate()) { |
|
return "Due: $status " . timeToHumanString($res->duedate($part)); |
|
} else { |
|
return "Open, no due date"; |
|
} |
} |
} |
if ($status == $res->PAST_DUE_ANSWER_LATER) { |
if ($status == $res->PAST_DUE_ANSWER_LATER) { |
return "Answer: " . timeToHumanString($res->answerdate($part)); |
return "Answer opens: " . timeToHumanString($res->answerdate($part)); |
} |
} |
if ($status == $res->PAST_DUE_NO_ANSWER) { |
if ($status == $res->PAST_DUE_NO_ANSWER) { |
return "Was Due: " . timeToHumanString($res->duedate($part)); |
return "Was Due: " . timeToHumanString($res->duedate($part)); |
Line 1057 sub getDescription {
|
Line 1235 sub getDescription {
|
if ($status == $res->ANSWER_OPEN) { |
if ($status == $res->ANSWER_OPEN) { |
return "Answer available"; |
return "Answer available"; |
} |
} |
|
if ($status == $res->EXCUSED) { |
|
return "Excused by instructor"; |
|
} |
|
if ($status == $res->TRIES_LEFT) { |
|
my $tries = $res->tries(); |
|
my $maxtries = $res->maxtries(); |
|
my $triesString = "($tries of $maxtries tries used)"; |
|
if ($res->duedate()) { |
|
return "Due: $status " . timeToHumanString($res->duedate($part)) . |
|
" $triesString"; |
|
} else { |
|
return "No due date $triesString"; |
|
} |
|
} |
} |
} |
|
|
# I want to change this into something more human-friendly. For |
# I want to change this into something more human-friendly. For |
# now, this is a simple call to localtime. The final function |
# now, this is a simple call to localtime. The final function |
# probably belongs in loncommon. |
# probably belongs in loncommon. |
sub timeToHumanString { |
sub timeToHumanString { |
return localtime(shift); |
my ($time) = @_; |
|
# zero, '0' and blank are bad times |
|
if ($time) { |
|
return localtime($time); |
|
} else { |
|
return 'Never'; |
|
} |
} |
} |
|
|
1; |
1; |
Line 1090 You must obtain resource objects through
|
Line 1288 You must obtain resource objects through
|
|
|
=over 4 |
=over 4 |
|
|
=item * B<new>(filename, parmHashFile, genCourseAndUserOptions): Binds a new navmap object to the compiled course representation and parmHashFile. genCourseAndUserOptions is a flag saying whether the course options and user options hash should be generated. This is for when you are using the parameters of the resources that require them; see documentation in resource object documentation. Returns the navmap object if this is successful, or B<undef> if not. You must check for undef; errors will occur when you try to use the other methods otherwise. |
=item * B<new>(filename, parmHashFile, genCourseAndUserOptions, genMailDiscussStatus): Binds a new navmap object to the compiled course representation and parmHashFile. genCourseAndUserOptions is a flag saying whether the course options and user options hash should be generated. This is for when you are using the parameters of the resources that require them; see documentation in resource object documentation. genMailDiscussStatus causes the nav map to retreive information about the email and discussion status of resources. Returns the navmap object if this is successful, or B<undef> if not. You must check for undef; errors will occur when you try to use the other methods otherwise. |
|
|
=item * B<getIterator>(first, finish, filter, condition): See iterator documentation below. |
=item * B<getIterator>(first, finish, filter, condition): See iterator documentation below. |
|
|
Line 1108 sub new {
|
Line 1306 sub new {
|
$self->{NAV_HASH_FILE} = shift; |
$self->{NAV_HASH_FILE} = shift; |
$self->{PARM_HASH_FILE} = shift; |
$self->{PARM_HASH_FILE} = shift; |
$self->{GENERATE_COURSE_USER_OPT} = shift; |
$self->{GENERATE_COURSE_USER_OPT} = shift; |
|
$self->{GENERATE_EMAIL_DISCUSS_STATUS} = shift; |
|
|
# Resource cache stores navmapresource's as we reference them. We generate |
# Resource cache stores navmapresource's as we reference them. We generate |
# them on-demand so we don't pay for creating resources unless we use them. |
# them on-demand so we don't pay for creating resources unless we use them. |
Line 1129 sub new {
|
Line 1328 sub new {
|
if (!(tie(%parmhash, 'GDBM_File', $self->{PARM_HASH_FILE}, |
if (!(tie(%parmhash, 'GDBM_File', $self->{PARM_HASH_FILE}, |
&GDBM_READER(), 0640))) |
&GDBM_READER(), 0640))) |
{ |
{ |
|
untie $self->{PARM_HASH}; |
return undef; |
return undef; |
} |
} |
$self->{PARM_HASH} = \%parmhash; |
$self->{PARM_HASH} = \%parmhash; |
Line 1185 sub new {
|
Line 1385 sub new {
|
$useropt{$userprefix.&Apache::lonnet::unescape($name)}= |
$useropt{$userprefix.&Apache::lonnet::unescape($name)}= |
&Apache::lonnet::unescape($value); |
&Apache::lonnet::unescape($value); |
} |
} |
$self->{COURSE_OPT} = \%courseopt; |
$self->{COURSE_OPT} = \%courseopt; |
$self->{USER_OPT} = \%useropt; |
$self->{USER_OPT} = \%useropt; |
} |
} |
} |
} |
|
|
|
if ($self->{GENERATE_EMAIL_DISCUSS_STATUS}) { |
|
my $cid=$ENV{'request.course.id'}; |
|
my ($cdom,$cnum)=split(/\_/,$cid); |
|
|
|
my %emailstatus = &Apache::lonnet::dump('email_status'); |
|
my $logoutTime = $emailstatus{'logout'}; |
|
my $courseLeaveTime = $emailstatus{'logout_'.$ENV{'request.course.id'}}; |
|
$self->{LAST_CHECK} = ($courseLeaveTime > $logoutTime ? |
|
$courseLeaveTime : $logoutTime); |
|
my %discussiontime = &Apache::lonnet::dump('discussiontimes', |
|
$cdom, $cnum); |
|
my %feedback=(); |
|
my %error=(); |
|
my $keys = &Apache::lonnet::reply('keys:'. |
|
$ENV{'user.domain'}.':'. |
|
$ENV{'user.name'}.':nohist_email', |
|
$ENV{'user.home'}); |
|
|
|
foreach my $msgid (split(/\&/, $keys)) { |
|
$msgid=&Apache::lonnet::unescape($msgid); |
|
my $plain=&Apache::lonnet::unescape(&Apache::lonnet::unescape($msgid)); |
|
if ($plain=~/(Error|Feedback) \[([^\]]+)\]/) { |
|
my ($what,$url)=($1,$2); |
|
my %status= |
|
&Apache::lonnet::get('email_status',[$msgid]); |
|
if ($status{$msgid}=~/^error\:/) { |
|
$status{$msgid}=''; |
|
} |
|
|
|
if (($status{$msgid} eq 'new') || |
|
(!$status{$msgid})) { |
|
if ($what eq 'Error') { |
|
$error{$url}.=','.$msgid; |
|
} else { |
|
$feedback{$url}.=','.$msgid; |
|
} |
|
} |
|
} |
|
} |
|
|
|
$self->{FEEDBACK} = \%feedback; |
|
$self->{ERROR_MSG} = \%error; # what is this? JB |
|
$self->{DISCUSSION_TIME} = \%discussiontime; |
|
$self->{EMAIL_STATUS} = \%emailstatus; |
|
|
|
} |
|
|
bless($self); |
bless($self); |
|
|
return $self; |
return $self; |
} |
} |
|
|
|
# Checks to see if coursemap is defined, matching test in old lonnavmaps |
|
sub courseMapDefined { |
|
my $self = shift; |
|
my $uri = &Apache::lonnet::clutter($ENV{'request.course.uri'}); |
|
|
|
my $firstres = $self->{NAV_HASH}->{"map_start_$uri"}; |
|
my $lastres = $self->{NAV_HASH}->{"map_finish_$uri"}; |
|
return $firstres && $lastres; |
|
} |
|
|
sub getIterator { |
sub getIterator { |
my $self = shift; |
my $self = shift; |
my $iterator = Apache::lonnavmaps::iterator->new($self, shift, shift, |
my $iterator = Apache::lonnavmaps::iterator->new($self, shift, shift, |
shift, shift); |
shift, undef, shift); |
return $iterator; |
return $iterator; |
} |
} |
|
|
# unties the hash when done |
# unties the hash when done |
sub untieHashes { |
sub untieHashes { |
my $self = shift; |
my $self = shift; |
untie $self->{NAV_HASH} if ($self->{HASH_TIED}); |
untie %{$self->{NAV_HASH}} if ($self->{HASH_TIED}); |
untie $self->{PARM_HASH} if ($self->{HASH_TIED}); |
untie %{$self->{PARM_HASH}} if ($self->{HASH_TIED}); |
$self->{HASH_TIED} = 0; |
$self->{HASH_TIED} = 0; |
} |
} |
|
|
Line 1216 sub DESTROY {
|
Line 1473 sub DESTROY {
|
$self->untieHashes(); |
$self->untieHashes(); |
} |
} |
|
|
|
# Private function: Does the given resource (as a symb string) have |
|
# current discussion? Returns 0 if chat/mail data not extracted. |
|
sub hasDiscussion { |
|
my $self = shift; |
|
my $symb = shift; |
|
if (!defined($self->{DISCUSSION_TIME})) { return 0; } |
|
|
|
return $self->{DISCUSSION_TIME}->{$symb} > |
|
$self->{LAST_CHECK}; |
|
} |
|
|
|
# Private function: Does the given resource (as a symb string) have |
|
# current feedback? Returns the string in the feedback hash, which |
|
# will be false if it does not exist. |
|
sub getFeedback { |
|
my $self = shift; |
|
my $symb = shift; |
|
|
|
if (!defined($self->{FEEDBACK})) { return ""; } |
|
|
|
return $self->{FEEDBACK}->{$symb}; |
|
} |
|
|
=pod |
=pod |
|
|
=item * B<getById>(id): Based on the ID of the resource (1.1, 3.2, etc.), get a resource object for that resource. This method, or other methods that use it (as in the resource object) is the only proper way to obtain a resource object. |
=item * B<getById>(id): Based on the ID of the resource (1.1, 3.2, etc.), get a resource object for that resource. This method, or other methods that use it (as in the resource object) is the only proper way to obtain a resource object. |
Line 1310 sub parmval {
|
Line 1590 sub parmval {
|
|
|
# ---------------------------------------------------------- first, check user |
# ---------------------------------------------------------- first, check user |
if ($uname and defined($useropt)) { |
if ($uname and defined($useropt)) { |
if ($$useropt{$courselevelr}) { return $$useropt{$courselevelr}; } |
if (defined($$useropt{$courselevelr})) { return $$useropt{$courselevelr}; } |
if ($$useropt{$courselevelm}) { return $$useropt{$courselevelm}; } |
if (defined($$useropt{$courselevelm})) { return $$useropt{$courselevelm}; } |
if ($$useropt{$courselevel}) { return $$useropt{$courselevel}; } |
if (defined($$useropt{$courselevel})) { return $$useropt{$courselevel}; } |
} |
} |
|
|
# ------------------------------------------------------- second, check course |
# ------------------------------------------------------- second, check course |
if ($csec and defined($courseopt)) { |
if ($csec and defined($courseopt)) { |
if ($$courseopt{$seclevelr}) { return $$courseopt{$seclevelr}; } |
if (defined($$courseopt{$seclevelr})) { return $$courseopt{$seclevelr}; } |
if ($$courseopt{$seclevelm}) { return $$courseopt{$seclevelm}; } |
if (defined($$courseopt{$seclevelm})) { return $$courseopt{$seclevelm}; } |
if ($$courseopt{$seclevel}) { return $$courseopt{$seclevel}; } |
if (defined($$courseopt{$seclevel})) { return $$courseopt{$seclevel}; } |
} |
} |
|
|
if (defined($courseopt)) { |
if (defined($courseopt)) { |
if ($$courseopt{$courselevelr}) { return $$courseopt{$courselevelr}; } |
if (defined($$courseopt{$courselevelr})) { return $$courseopt{$courselevelr}; } |
if ($$courseopt{$courselevelm}) { return $$courseopt{$courselevelm}; } |
if (defined($$courseopt{$courselevelm})) { return $$courseopt{$courselevelm}; } |
if ($$courseopt{$courselevel}) { return $$courseopt{$courselevel}; } |
if (defined($$courseopt{$courselevel})) { return $$courseopt{$courselevel}; } |
} |
} |
|
|
# ----------------------------------------------------- third, check map parms |
# ----------------------------------------------------- third, check map parms |
|
|
my $thisparm=$$parmhash{$symbparm}; |
my $thisparm=$$parmhash{$symbparm}; |
if ($thisparm) { return $thisparm; } |
if (defined($thisparm)) { return $thisparm; } |
|
|
# ----------------------------------------------------- fourth , check default |
# ----------------------------------------------------- fourth , check default |
|
|
my $default=&Apache::lonnet::metadata($fn,$rwhat.'.default'); |
my $default=&Apache::lonnet::metadata($fn,$rwhat.'.default'); |
if ($default) { return $default} |
if (defined($default)) { return $default} |
|
|
# --------------------------------------------------- fifth , cascade up parts |
# --------------------------------------------------- fifth , cascade up parts |
|
|
Line 1346 sub parmval {
|
Line 1626 sub parmval {
|
my ($part,$id)=split(/\_/,$space); |
my ($part,$id)=split(/\_/,$space); |
if ($id) { |
if ($id) { |
my $partgeneral=$self->parmval($part.".$qualifier",$symb); |
my $partgeneral=$self->parmval($part.".$qualifier",$symb); |
if ($partgeneral) { return $partgeneral; } |
if (defined($partgeneral)) { return $partgeneral; } |
} else { |
} else { |
my $resourcegeneral=$self->parmval("0.$qualifier",$symb); |
my $resourcegeneral=$self->parmval("0.$qualifier",$symb); |
if ($resourcegeneral) { return $resourcegeneral; } |
if (defined($resourcegeneral)) { return $resourcegeneral; } |
} |
} |
} |
} |
return ''; |
return ''; |
Line 1384 getIterator behaves as follows:
|
Line 1664 getIterator behaves as follows:
|
|
|
=over 4 |
=over 4 |
|
|
=item B<getIterator>(nav_map, firstResource, finishResource, filterHash): All parameters are optional. firstResource is a resource reference corresponding to where the iterator should start. It defaults to navmap->firstResource() for the corresponding nav map. finishResource corresponds to where you want the iterator to end, defaulting to navmap->finishResource(). It is your responsibility to ensure that the iterator will actually get there. filterHash is a hash used as a set containing strings representing the resource IDs, defaulting to empty. Condition is a 1 or 0 that sets what to do with the filter hash: If a 1, then only resource that exist IN the filterHash will be recursed on. If it is a 0, only resources NOT in the filterHash will be recursed on. Defaults to 0. |
=item B<getIterator>(nav_map, firstResource, finishResource, filterHash, condition): All parameters are optional. firstResource is a resource reference corresponding to where the iterator should start. It defaults to navmap->firstResource() for the corresponding nav map. finishResource corresponds to where you want the iterator to end, defaulting to navmap->finishResource(). It is your responsibility to ensure that the iterator will actually get there. filterHash is a hash used as a set containing strings representing the resource IDs, defaulting to empty. Condition is a 1 or 0 that sets what to do with the filter hash: If a 0, then only resource that exist IN the filterHash will be recursed on. If it is a 1, only resources NOT in the filterHash will be recursed on. Defaults to 0, which is to say, do not recurse unless explicitly asked to. |
|
|
Thus, by default, all resources will be shown. Change the condition to a 1 without changing the hash, and only the top level of the map will be shown. Changing the condition to 1 and including some values in the hash will allow you to selectively examine parts of the navmap, while leaving it on 0 and adding things to the hash will allow you to selectively ignore parts of the nav map. See the handler code for examples. |
Thus, by default, all resources will be shown. Change the condition to a 1 without changing the hash, and only the top level of the map will be shown. Changing the condition to 1 and including some values in the hash will allow you to selectively examine parts of the navmap, while leaving it on 0 and adding things to the hash will allow you to selectively ignore parts of the nav map. See the handler code for examples. |
|
|
Line 1436 sub new {
|
Line 1716 sub new {
|
# A hash, used as a set, of resource already seen |
# A hash, used as a set, of resource already seen |
$self->{ALREADY_SEEN} = shift; |
$self->{ALREADY_SEEN} = shift; |
if (!defined($self->{ALREADY_SEEN})) { $self->{ALREADY_SEEN} = {} }; |
if (!defined($self->{ALREADY_SEEN})) { $self->{ALREADY_SEEN} = {} }; |
|
$self->{CONDITION} = shift; |
|
|
# Flag: Have we started yet? If not, the first action is to return BEGIN_MAP. |
# Flag: Have we started yet? If not, the first action is to return BEGIN_MAP. |
$self->{STARTED} = 0; |
$self->{STARTED} = 0; |
Line 1606 sub next {
|
Line 1887 sub next {
|
|
|
# If this is a map and we want to recurse down it... (not filtered out) |
# If this is a map and we want to recurse down it... (not filtered out) |
if ($self->{HERE}->is_map() && |
if ($self->{HERE}->is_map() && |
!defined($self->{FILTER}->{$self->{HERE}->map_pc()})) { |
(defined($self->{FILTER}->{$self->{HERE}->map_pc()}) xor $self->{CONDITION})) { |
$self->{RECURSIVE_ITERATOR_FLAG} = 1; |
$self->{RECURSIVE_ITERATOR_FLAG} = 1; |
my $firstResource = $self->{HERE}->map_start(); |
my $firstResource = $self->{HERE}->map_start(); |
my $finishResource = $self->{HERE}->map_finish(); |
my $finishResource = $self->{HERE}->map_finish(); |
Line 1616 sub next {
|
Line 1897 sub next {
|
# isn't MAIN::new, __PACKAGE__::new or Apache::lonnavmaps::iterator->new |
# isn't MAIN::new, __PACKAGE__::new or Apache::lonnavmaps::iterator->new |
$self->{RECURSIVE_ITERATOR} = |
$self->{RECURSIVE_ITERATOR} = |
Apache::lonnavmaps::iterator->new ($self->{NAV_MAP}, $firstResource, |
Apache::lonnavmaps::iterator->new ($self->{NAV_MAP}, $firstResource, |
$finishResource, $self->{FILTER}, $self->{ALREADY_SEEN}); |
$finishResource, $self->{FILTER}, $self->{ALREADY_SEEN}, |
# prime the new iterator with the first resource |
$self->{CONDITION}); |
#push @{$self->{RECURSIVE_ITERATOR}->{BRANCH_STACK}}, $firstResource; |
|
} |
} |
|
|
return $self->{HERE}; |
return $self->{HERE}; |
Line 1948 Misc. functions for the resource.
|
Line 2228 Misc. functions for the resource.
|
|
|
=over 4 |
=over 4 |
|
|
|
=item * B<hasDiscussion>: Returns a false value if there has been discussion since the user last logged in, true if there has. Always returns false if the discussion data was not extracted when the nav map was constructed. |
|
|
|
=item * B<getFeedback>: Gets the feedback for the resource and returns the raw feedback string for the resource, or the null string if there is no feedback or the email data was not extracted when the nav map was constructed. Usually used like this: |
|
|
|
for (split(/\,/, $res->getFeedback())) { |
|
my $link = &Apache::lonnet::escape($_); |
|
... |
|
|
|
and use the link as appropriate. |
|
|
|
=cut |
|
|
|
sub hasDiscussion { |
|
my $self = shift; |
|
return $self->{NAV_MAP}->hasDiscussion($self->symb()); |
|
} |
|
|
|
sub getFeedback { |
|
my $self = shift; |
|
return $self->{NAV_MAP}->getFeedback($self->symb()); |
|
} |
|
|
|
=pod |
|
|
=item * B<parts>(): Returns a list reference containing sorted strings corresponding to each part of the problem. To count the number of parts, use the list in a scalar context, and subtract one if greater then two. (One part problems have a part 0. Multi-parts have a part 0, plus a part for each part. You may or may not wish to filter out part 0.) |
=item * B<parts>(): Returns a list reference containing sorted strings corresponding to each part of the problem. To count the number of parts, use the list in a scalar context, and subtract one if greater then two. (One part problems have a part 0. Multi-parts have a part 0, plus a part for each part. You may or may not wish to filter out part 0.) |
|
|
=item * B<countParts>(): Returns the number of parts of the problem a student can answer. Thus, for single part problems, returns 1. For multipart, it returns the number of parts in the problem, not including psuedo-part 0. |
=item * B<countParts>(): Returns the number of parts of the problem a student can answer. Thus, for single part problems, returns 1. For multipart, it returns the number of parts in the problem, not including psuedo-part 0. |
Line 2015 Idiomatic usage of these two methods wou
|
Line 2319 Idiomatic usage of these two methods wou
|
|
|
=over 4 |
=over 4 |
|
|
=item * B<getDateStatus>($part): ($part defaults to 0). A convenience function that returns a symbolic constant telling you about the date status of the part, with the following possible results: |
=item * B<getDateStatus>($part): ($part defaults to 0). A convenience function that returns a symbolic constant telling you about the date status of the part. You should still check to see if there is a due date at all, if you care about that. The possible return values are: |
|
|
=back |
=back |
|
|
Line 2027 B<Date Codes>
|
Line 2331 B<Date Codes>
|
|
|
=item * B<OPEN>: Open and not yet due. |
=item * B<OPEN>: Open and not yet due. |
|
|
|
|
=item * B<PAST_DUE_ANSWER_LATER>: The due date has passed, but the answer date has not yet arrived. |
=item * B<PAST_DUE_ANSWER_LATER>: The due date has passed, but the answer date has not yet arrived. |
|
|
=item * B<PAST_DUE_NO_ANSWER>: The due date has passed and there is no answer opening date set. |
=item * B<PAST_DUE_NO_ANSWER>: The due date has passed and there is no answer opening date set. |
Line 2074 sub getDateStatus {
|
Line 2379 sub getDateStatus {
|
# should this be the same as "open later"? think multipart. |
# should this be the same as "open later"? think multipart. |
return $self->NOTHING_SET; |
return $self->NOTHING_SET; |
} |
} |
if (!$open || $now < $open) {return $self->OPEN_LATER}; |
if (!$open || $now < $open) {return $self->OPEN_LATER} |
if (!$due || $now < $due) {return $self->OPEN}; |
if (!$due || $now < $due) {return $self->OPEN} |
if ($answer && $now < $answer) {return $self->PAST_DUE_ANSWER_LATER}; |
if ($answer && $now < $answer) {return $self->PAST_DUE_ANSWER_LATER} |
if ($answer) { return $self->ANSWER_OPEN; }; |
if ($answer) { return $self->ANSWER_OPEN; } |
return PAST_DUE_NO_ANSWER; |
return PAST_DUE_NO_ANSWER; |
} |
} |
|
|
Line 2150 Along with directly returning the date o
|
Line 2455 Along with directly returning the date o
|
|
|
=item * NETWORK_FAILURE: The network has failed and the information is not available. |
=item * NETWORK_FAILURE: The network has failed and the information is not available. |
|
|
=item * NOTHING_SET: No dates have been set for this problem (part) at all. (Because only certain parts of a multi-part problem may be assigned, this can not be collapsed into "open later", as we don't know a given part will EVER be opened.) |
=item * NOTHING_SET: No dates have been set for this problem (part) at all. (Because only certain parts of a multi-part problem may be assigned, this can not be collapsed into "open later", as we don't know a given part will EVER be opened. For single part, this is the same as "OPEN_LATER".) |
|
|
=item * CORRECT: For any reason at all, the part is considered correct. |
=item * CORRECT: For any reason at all, the part is considered correct. |
|
|
Line 2208 sub status {
|
Line 2513 sub status {
|
# by 4 matrix (date status). |
# by 4 matrix (date status). |
|
|
if ($dateStatus == PAST_DUE_ANSWER_LATER || |
if ($dateStatus == PAST_DUE_ANSWER_LATER || |
$dateStatus == PAST_DUE_NO_ANSWER) { |
$dateStatus == PAST_DUE_NO_ANSWER ) { |
return $dateStatus; |
return $dateStatus; |
} |
} |
|
|
Line 2260 sub getNext {
|
Line 2565 sub getNext {
|
my $next = $choice->goesto(); |
my $next = $choice->goesto(); |
$next = $self->{NAV_MAP}->getById($next); |
$next = $self->{NAV_MAP}->getById($next); |
|
|
# Don't remember it if we've already seen it |
# Don't remember it if we've already seen it or if |
|
# the student doesn't have browse priviledges |
|
my $browsePriv = &Apache::lonnet::allowed('bre', $self->src); |
if (!defined($alreadySeenHash) || |
if (!defined($alreadySeenHash) || |
!defined($alreadySeenHash->{$next->{ID}})) { |
!defined($alreadySeenHash->{$next->{ID}}) || |
|
($browsePriv ne '2' && $browsePriv ne 'F')) { |
push @branches, $next; |
push @branches, $next; |
} |
} |
} |
} |