--- loncom/interface/lonnavmaps.pm 2004/08/13 20:12:11 1.275 +++ loncom/interface/lonnavmaps.pm 2004/12/07 22:58:05 1.307 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.275 2004/08/13 20:12:11 albertel Exp $ +# $Id: lonnavmaps.pm,v 1.307 2004/12/07 22:58:05 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -33,9 +33,11 @@ use strict; use Apache::Constants qw(:common :http); use Apache::loncommon(); use Apache::lonmenu(); +use Apache::lonenc(); use Apache::lonlocal; +use Apache::lonnet; use POSIX qw (floor strftime); -use Data::Dumper; # for debugging, not always used +use Data::Dumper; # for debugging, not always # symbolic constants sub SYMB { return 1; } @@ -84,7 +86,7 @@ my %colormap = my $hurryUpColor = "#FF0000"; sub launch_win { - my ($mode,$script)=@_; + my ($mode,$script,$toplinkitems)=@_; my $result; if ($script ne 'no') { $result.=''; } if ($mode eq 'link') { - $result.='' - .&mt("Launch navigation in separate window")." "; + &add_linkitem($toplinkitems,'launchnav','launch_navmapwin()', + "Launch navigation window"); } return $result; } @@ -161,6 +163,8 @@ sub real_handler { &Apache::loncommon::no_cache($r); $r->send_http_header; + my %toplinkitems=(); + if ($ENV{QUERY_STRING} eq 'collapseExternal') { &Apache::lonnet::put('environment',{'remotenavmap' => 'off'}); &Apache::lonnet::appenv('environment.remotenavmap' => 'off'); @@ -172,6 +176,13 @@ sub real_handler { swmenu.clearTimeout(swmenu.menucltim); $navstatus MENU + } else { + my $nothing = &Apache::lonhtmlcommon::javascript_nothing(); + my $mainwindow='window.open('.$nothing.',"loncapaclient","",false);'; + $menu=(<print(<<"ENDSUBM"); @@ -206,11 +217,12 @@ ENDSUBM $r->print("\n"); $r->print("".&mt('Navigate Course Contents').""); # ------------------------------------------------------------ Get query string - &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['register','sort']); + &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['register','sort','showOnlyHomework']); # ----------------------------------------------------- Force menu registration my $addentries=''; my $more_unload; + my $body_only=''; if ($ENV{'environment.remotenavmap'} eq 'on') { $r->print(''); # FIXME need to be smarter to only catch window close events # $more_unload="collapse()" + $body_only=1; } if ($ENV{'form.register'}) { $addentries=' onLoad="'.&Apache::lonmenu::loadevents(). @@ -232,14 +245,15 @@ ENDSUBM # Header $r->print(''. &Apache::loncommon::bodytag('Navigate Course Contents','', - $addentries,'','',$ENV{'form.register'})); - $r->print(''. - &Apache::loncommon::help_open_menu('','Navigation Screen','Navigation_Screen','',undef,'RAT')); + $addentries,$body_only,'', + $ENV{'form.register'})); + $r->print(''); $r->rflush(); # Check that it's defined if (!($navmap->courseMapDefined())) { + $r->print(&Apache::loncommon::help_open_menu('','Navigation Screen','Navigation_Screen','',undef,'RAT')); $r->print('Coursemap undefined.' . ''); return OK; @@ -281,18 +295,16 @@ ENDSUBM } if ($ENV{'environment.remotenavmap'} ne 'on') { - $r->print(&launch_win('link','yes')); + $r->print(&launch_win('link','yes',\%toplinkitems)); } if ($ENV{'environment.remotenavmap'} eq 'on') { -# $r->print("" . - $r->print("" . - &mt("Close separate navigation window"). - "    "); + &add_linkitem(\%toplinkitems,'closenav','collapse()', + "Close navigation window"); } my $jumpToFirstHomework = 0; # Check to see if the student is jumping to next open, do-able problem - if ($ENV{QUERY_STRING} eq 'jumpToFirstHomework') { + if ($ENV{QUERY_STRING} =~ /^jumpToFirstHomework/) { $jumpToFirstHomework = 1; # Find the next homework problem that they can do. my $iterator = $navmap->getIterator(undef, undef, undef, 1); @@ -325,8 +337,9 @@ ENDSUBM $r->print("All homework assignments have been completed.

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

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

"); $ENV{'form.filter'} = ''; $ENV{'form.condition'} = 1; $resource_no_folder_link = 1; } else { - $r->print("" . - &mt("Show Only Uncompleted Homework")."    "); + &add_linkitem(\%toplinkitems,'uncompleted', + 'location.href="navmaps?sort='.$ENV{'form.sort'}. + '&showOnlyHomework=1"', + "Show Only Uncompleted Homework"); } - $r->print("
+ my %selected=($ENV{'form.sort'} => 'selected=on'); + my $sort_html=(" - Sort by: + + -
"); # renderer call - my $renderArgs = { 'cols' => [0,2,3], + my $renderArgs = { 'cols' => [0,1,2,3], 'sort' => $ENV{'form.sort'}, 'url' => '/adm/navmaps', 'navmap' => $navmap, @@ -370,9 +390,11 @@ ENDSUBM 'suppressEmptySequences' => $suppressEmptySequences, 'filterFunc' => $filterFunc, 'resource_no_folder_link' => $resource_no_folder_link, - 'r' => $r}; + 'sort_html'=> $sort_html, + 'r' => $r, + 'caller' => 'navmapsdisplay', + 'linkitems' => \%toplinkitems}; my $render = render($renderArgs); - $navmap->untieHashes(); # If no resources were printed, print a reassuring message so the # user knows there was no error. @@ -422,7 +444,7 @@ sub getLinkForResource { foreach $res (@$stack) { if (defined($res)) { if ($res->is_page()) { - return $res->src(); + return $res->link(); } # in case folder was skipped over as "only sequence" my ($map,$id,$src)=&Apache::lonnet::decode_symb($res->symb()); @@ -441,7 +463,7 @@ sub getLinkForResource { if (defined($_)) { $res = $_; } } - return $res->src(); + return $res->link(); } # Convenience function: This separates the logic of how to create @@ -642,7 +664,7 @@ sub timeToHumanString { } # Not this year, so show the year - my $timeStr = strftime("on %A, %b %e %G at %I:%M %P", localtime($time)); + my $timeStr = strftime("on %A, %b %e %Y at %I:%M %P", localtime($time)); $timeStr =~ s/12:00 am/00:00/; $timeStr =~ s/12:00 pm/noon/; return $timeStr; @@ -989,6 +1011,15 @@ sub render_resource { my $nonLinkedText = ''; # stuff after resource title not in link my $link = $params->{"resourceLink"}; + + # The URL part is not escaped at this point, but the symb is... + # The stuff to the left of the ? must have ' replaced by \' since + # it will be quoted with ' in the href. + + my ($left,$right) = split(/\?/, $link); + $left =~ s/'/\\'/g; + $link = $left.'?'.$right; + my $src = $resource->src(); my $it = $params->{"iterator"}; my $filter = $it->{FILTER}; @@ -997,28 +1028,31 @@ sub render_resource { my $partLabel = ""; my $newBranchText = ""; - + my $location=&Apache::loncommon::lonhttpdurl("/adm/lonIcons"); # If this is a new branch, label it so if ($params->{'isNewBranch'}) { - $newBranchText = ""; + $newBranchText = ""; } # links to open and close the folder + + my $linkopen = ""; + + my $linkclose = ""; # Default icon: unknown page - my $icon = ""; + my $icon = ""; if ($resource->is_problem()) { if ($part eq '0' || $params->{'condensed'}) { - $icon = ''; + $icon =''; } else { $icon = $params->{'indentString'}; } } else { - $icon = ""; + $icon = ""; } # Display the correct map icon to open or shut map @@ -1033,7 +1067,7 @@ sub render_resource { if (!$params->{'resource_no_folder_link'}) { $icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . '.gif'; - $icon = ""; + $icon = ""; $linkopen = "{'queryString'} . '&filter='; @@ -1046,11 +1080,12 @@ sub render_resource { '&jump=' . &Apache::lonnet::escape($resource->symb()) . "&folderManip=1'>"; + } else { # Don't allow users to manipulate folder $icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . '.nomanip.gif'; - $icon = ""; + $icon = ""; $linkopen = ""; $linkclose = ""; @@ -1073,6 +1108,7 @@ sub render_resource { } # Decide what to display + $result .= "$newBranchText$linkopen$icon$linkclose"; my $curMarkerBegin = ''; @@ -1118,10 +1154,10 @@ sub render_communication_status { my $link = $params->{"resourceLink"}; my $linkopen = ""; my $linkclose = ""; - + my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc"); if ($resource->hasDiscussion()) { $discussionHTML = $linkopen . - '' . + '' . $linkclose; } @@ -1131,7 +1167,7 @@ sub render_communication_status { if ($_) { $feedbackHTML .= ' ' - . ''; } } @@ -1146,7 +1182,7 @@ sub render_communication_status { $errorcount++; $errorHTML .= ' ' - . ''; } } @@ -1175,7 +1211,9 @@ sub render_quick_status { my $icon = $statusIconMap{$resource->simpleStatus($part)}; my $alt = $iconAltTags{$icon}; if ($icon) { - $result .= "$linkopen$alt$linkclose\n"; + my $location= + &Apache::loncommon::lonhttpdurl("/adm/lonIcons/$icon"); + $result .= "$linkopen$alt$linkclose\n"; } else { $result .= " \n"; } @@ -1306,11 +1344,17 @@ sub setDefault { return $val; } +sub cmp_title { + my ($atitle,$btitle) = (lc($_[0]->compTitle),lc($_[1]->compTitle)); + $atitle=~s/^\s*//; + $btitle=~s/^\s*//; + return $atitle cmp $btitle; +} + sub render { my $args = shift; &Apache::loncommon::get_unprocessed_cgi($ENV{QUERY_STRING}); my $result = ''; - # Configure the renderer. my $cols = $args->{'cols'}; if (!defined($cols)) { @@ -1373,7 +1417,7 @@ sub render { # Determine where the "here" marker is and where the screen jumps to. if ($ENV{'form.postsymb'}) { - $here = $jump = $ENV{'form.postsymb'}; + $here = $jump = &Apache::lonnet::symbclean($ENV{'form.postsymb'}); } elsif ($ENV{'form.postdata'}) { # couldn't find a symb, is there a URL? my $currenturl = $ENV{'form.postdata'}; @@ -1381,10 +1425,7 @@ sub render { #$currenturl=~s/^[^\/]+//; $here = $jump = &Apache::lonnet::symbread($currenturl); - } else { - &Apache::lonnet::logthis("Hrrm,"); - - } + } # Step three: Ensure the folders are open my $mapIterator = $navmap->getIterator(undef, undef, undef, 1); @@ -1394,7 +1435,7 @@ sub render { # We only need to do this if we need to open the maps to show the # current position. This will change the counter so we can't count # for the jump marker with this loop. - while (($curRes = $mapIterator->next()) && !$found) { + while ($here && ($curRes = $mapIterator->next()) && !$found) { if (ref($curRes) && $curRes->symb() eq $here) { my $mapStack = $mapIterator->getStack(); @@ -1470,23 +1511,24 @@ sub render { my $printKey = $args->{'printKey'}; my $printCloseAll = $args->{'printCloseAll'}; if (!defined($printCloseAll)) { $printCloseAll = 1; } - + # Print key? if ($printKey) { $result .= ''; my $date=localtime; $result.=''; + my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc"); if ($navmap->{LAST_CHECK}) { $result .= - ' '.&mt('New discussion since').' '. + ' '.&mt('New discussion since').' '. strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})). ''; } else { $result .= ''; } @@ -1494,51 +1536,86 @@ sub render { } if ($printCloseAll && !$args->{'resource_no_folder_link'}) { + my ($link,$text); if ($condition) { - $result.="".&mt('Close All Folders').""; + $link='"navmaps?condition=0&filter=&'.$queryString. + '&here='.&Apache::lonnet::escape($here).'"'; + $text='Close All Folders'; } else { - $result.="".&mt('Open All Folders').""; - } + $link='"navmaps?condition=1&filter=&'.$queryString. + '&here='.&Apache::lonnet::escape($here).'"'; + $text='Open All Folders'; + } + if ($args->{'caller'} eq 'navmapsdisplay') { + &add_linkitem($args->{'linkitems'},'changefolder', + 'location.href='.$link,$text); + } else { + $result.=''.&mt($text).''; + } $result .= "\n"; } # Check for any unread discussions in all resources. - if (!$args->{'resource_no_folder_link'}) { - my $totdisc = 0; - my $haveDisc = ''; - my @allres=$navmap->retrieveResources(); - foreach my $resource (@allres) { - if ($resource->hasDiscussion()) { - my $ressymb; - if ($resource->symb() =~ m-(___adm/\w+/\w+)/(\d+)/bulletinboard$-) { - $ressymb = 'bulletin___'.$2.$1.'/'.$2.'/bulletinboard'; - } else { - $ressymb = $resource->symb(); + if ($args->{'caller'} eq 'navmapsdisplay') { + &add_linkitem($args->{'linkitems'},'clearbubbles', + 'document.clearbubbles.submit()', + 'Mark all posts read'); + my $time=time; + $result .= (< + + +END + if ($args->{'sort'} eq 'discussion') { + my $totdisc = 0; + my $haveDisc = ''; + my @allres=$navmap->retrieveResources(); + foreach my $resource (@allres) { + if ($resource->hasDiscussion()) { + my $ressymb; + if ($resource->symb() =~ m-(___adm/\w+/\w+)/(\d+)/bulletinboard$-) { + $ressymb = 'bulletin___'.$2.$1.'/'.$2.'/bulletinboard'; + } else { + $ressymb = $resource->symb(); + } + $haveDisc .= $ressymb.':'; + $totdisc ++; } - $haveDisc .= $ressymb.':'; - $totdisc ++; } - } - if ($totdisc > 0) { - $haveDisc =~ s/:$//; - my %lt = &Apache::lonlocal::texthash( - 'mapr' => 'Mark all posts read', - ); - $result .= (<$lt{'mapr'}  -
- - - + if ($totdisc > 0) { + $haveDisc =~ s/:$//; + $result .= (< + END + } + } + $result.=''; + } + + if ($args->{'caller'} eq 'navmapsdisplay') { + $result .= '
Key:    '. - ' '.&mt('New message (click to open)').'

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

'. '

  '. - ' '.&mt('Discussions').''. - '   '.&mt('New message (click to open)'). + ' '.&mt('Discussions').''. + '   '.&mt('New message (click to open)'). '
'; + if ($ENV{'environment.remotenavmap'} ne 'on') { + $result .= ''; } else { - $result .= '
'; + $result .= ''; + } + $result.=&show_linkitems($args->{'linkitems'}); + if ($args->{'sort_html'}) { + if ($ENV{'environment.remotenavmap'} ne 'on') { + $result.=''. + ''; + } else { + $result.=''; + } } + $result .= '
'. + &Apache::loncommon::help_open_menu('','Navigation Screen','Navigation_Screen','',undef,'RAT').' 
   '.$args->{'sort_html'}.'

'. + $args->{'sort_html'}.'
'; + } elsif ($args->{'sort_html'}) { + $result.=$args->{'sort_html'}; } + $result .= "
\n"; if ($r) { $r->print($result); @@ -1561,7 +1638,9 @@ END $args->{'indentLevel'} = 0; $args->{'isNewBranch'} = 0; $args->{'condensed'} = 0; - $args->{'indentString'} = setDefault($args->{'indentString'}, ""); + my $location= + &Apache::loncommon::lonhttpdurl("/adm/lonIcons/whitespace1.gif"); + $args->{'indentString'} = setDefault($args->{'indentString'}, ""); $args->{'displayedHereMarker'} = 0; # If we're suppressing empty sequences, look for them here. Use DFS for speed, @@ -1614,22 +1693,49 @@ END my @resources; my $code='';# sub { !(shift->is_map();) }; if ($args->{'sort'} eq 'title') { - @resources=$navmap->retrieveResources(undef, - sub { !shift->is_map(); }); - @resources= sort {lc($a->compTitle) cmp lc($b->compTitle)} @resources; + my $oldFilterFunc = $filterFunc; + my $filterFunc= + sub { + my ($res)=@_; + if ($res->is_map()) { return 0;} + return &$oldFilterFunc($res); + }; + @resources=$navmap->retrieveResources(undef,$filterFunc); + @resources= sort { &cmp_title($a,$b) } @resources; } elsif ($args->{'sort'} eq 'duedate') { - @resources=$navmap->retrieveResources(undef, - sub { shift->is_problem(); }); - @resources= sort - { + my $oldFilterFunc = $filterFunc; + my $filterFunc= + sub { + my ($res)=@_; + if (!$res->is_problem()) { return 0;} + return &$oldFilterFunc($res); + }; + @resources=$navmap->retrieveResources(undef,$filterFunc); + @resources= sort { if ($a->duedate ne $b->duedate) { return $a->duedate cmp $b->duedate; - } else { - lc($a->compTitle) cmp lc($b->compTitle) } + my $value=&cmp_title($a,$b); + return $value; } @resources; + } elsif ($args->{'sort'} eq 'discussion') { + my $oldFilterFunc = $filterFunc; + my $filterFunc= + sub { + my ($res)=@_; + if (!$res->hasDiscussion() && + !$res->getFeedback() && + !$res->getErrors()) { return 0;} + return &$oldFilterFunc($res); + }; + @resources=$navmap->retrieveResources(undef,$filterFunc); + @resources= sort { &cmp_title($a,$b) } @resources; + } else { + #unknow sort mechanism or default + undef($args->{'sort'}); } + while (1) { if ($args->{'sort'}) { $curRes = shift(@resources); @@ -1765,7 +1871,7 @@ END my $srcHasQuestion = $src =~ /\?/; $args->{"resourceLink"} = $src. ($srcHasQuestion?'&':'?') . - 'symb=' . &Apache::lonnet::escape($curRes->symb()). + 'symb=' . &Apache::lonnet::escape($curRes->shown_symb()). $anchor; # Now, display each column. @@ -1833,8 +1939,46 @@ if (location.href.indexOf('#curloc')==-1 $r->rflush(); } - if ($mustCloseNavMap) { $navmap->untieHashes(); } + return $result; +} + +sub add_linkitem { + my ($linkitems,$name,$cmd,$text)=@_; + $$linkitems{$name}{'cmd'}=$cmd; + $$linkitems{$name}{'text'}=&mt($text); +} +sub show_linkitems { + my ($linkitems)=@_; + my @linkorder = ("launchnav","closenav","firsthomework","everything", + "uncompleted","changefolder","clearbubbles"); + + my $result .= (< + +
+   +
'."\n"; + return $result; } @@ -1895,10 +2039,6 @@ successful, or B if not. =back -When you are done with the $navmap object, you I call -$navmap->untieHashes(), or you'll prevent the current user from using that -course until the web server is restarted. (!) - =head2 Methods =over 4 @@ -2044,17 +2184,11 @@ sub generate_email_discuss_status { foreach my $msgid (split(/\&/, $keys)) { $msgid=&Apache::lonnet::unescape($msgid); - my $plain=&Apache::lonnet::unescape(&Apache::lonnet::unescape($msgid)); - if ($plain=~/(Error|Feedback) \[([^\]]+)\]/) { - my ($what,$url)=($1,$2); - my %status= - &Apache::lonnet::get('email_status',[$msgid]); - if ($status{$msgid}=~/^error\:/) { - $status{$msgid}=''; - } - - if (($status{$msgid} eq 'new') || - (!$status{$msgid})) { + if ((!$emailstatus{$msgid}) || ($emailstatus{$msgid} eq 'new')) { + my $plain= + &Apache::lonnet::unescape(&Apache::lonnet::unescape($msgid)); + if ($plain=~/(Error|Feedback) \[([^\]]+)\]/) { + my ($what,$url)=($1,$2); if ($what eq 'Error') { $error{$url}.=','.$msgid; } else { @@ -2064,8 +2198,10 @@ sub generate_email_discuss_status { } } + #url's of resources that have feedbacks $self->{FEEDBACK} = \%feedback; - $self->{ERROR_MSG} = \%error; # what is this? JB + #or errors + $self->{ERROR_MSG} = \%error; $self->{DISCUSSION_TIME} = \%discussiontime; $self->{EMAIL_STATUS} = \%emailstatus; $self->{LAST_READ} = \%lastreadtime; @@ -2119,13 +2255,6 @@ sub getIterator { return $iterator; } -# unties the hash when done -sub untieHashes { - my $self = shift; - untie %{$self->{NAV_HASH}}; - untie %{$self->{PARM_HASH}}; -} - # Private method: Does the given resource (as a symb string) have # current discussion? Returns 0 if chat/mail data not extracted. sub hasDiscussion { @@ -2222,9 +2351,14 @@ sub getById { sub getBySymb { my $self = shift; my $symb = shift; + my ($mapUrl, $id, $filename) = &Apache::lonnet::decode_symb($symb); my $map = $self->getResourceByUrl($mapUrl); - return $self->getById($map->map_pc() . '.' . $id); + my $returnvalue = undef; + if (ref($map)) { + $returnvalue = $self->getById($map->map_pc() .'.'.$id); + } + return $returnvalue; } sub getByMapPc { @@ -2418,6 +2552,7 @@ in the filter function. =cut + sub getResourceByUrl { my $self = shift; my $resUrl = shift; @@ -2500,7 +2635,7 @@ sub hasResource { 1; package Apache::lonnavmaps::iterator; - +use WeakRef; =pod =back @@ -2640,7 +2775,7 @@ sub new { my $class = ref($proto) || $proto; my $self = {}; - $self->{NAV_MAP} = shift; + weaken($self->{NAV_MAP} = shift); return undef unless ($self->{NAV_MAP}); # Handle the parameters @@ -2976,7 +3111,7 @@ sub populateStack { 1; package Apache::lonnavmaps::DFSiterator; - +use WeakRef; # Not documented in the perldoc: This is a simple iterator that just walks # through the nav map and presents the resources in a depth-first search # fashion, ignorant of conditionals, randomized resources, etc. It presents @@ -3004,7 +3139,7 @@ sub new { my $class = ref($proto) || $proto; my $self = {}; - $self->{NAV_MAP} = shift; + weaken($self->{NAV_MAP} = shift); return undef unless ($self->{NAV_MAP}); $self->{FIRST_RESOURCE} = shift || $self->{NAV_MAP}->firstResource(); @@ -3158,7 +3293,7 @@ sub populateStack { 1; package Apache::lonnavmaps::resource; - +use WeakRef; use Apache::lonnet; =pod @@ -3240,7 +3375,7 @@ sub new { my $class = ref($proto) || $proto; my $self = {}; - $self->{NAV_MAP} = shift; + weaken($self->{NAV_MAP} = shift); $self->{ID} = shift; # Store this new resource in the parent nav map's cache. @@ -3328,6 +3463,7 @@ Returns the title of the resource. # These info functions can be used directly, as they don't return # resource information. sub comesfrom { my $self=shift; return $self->navHash("comesfrom_", 1); } +sub encrypted { my $self=shift; return $self->navHash("encrypted_", 1); } sub ext { my $self=shift; return $self->navHash("ext_", 1) eq 'true:'; } sub from { my $self=shift; return $self->navHash("from_", 1); } # considered private and undocumented @@ -3339,10 +3475,20 @@ sub randompick { return $self->{NAV_MAP}->{PARM_HASH}->{$self->symb . '.0.parameter_randompick'}; } +sub link { + my $self=shift; + if ($self->encrypted()) { return &Apache::lonenc::encrypted($self->src); } + return $self->src; +} sub src { my $self=shift; return $self->navHash("src_", 1); } +sub shown_symb { + my $self=shift; + if ($self->encrypted()) {return &Apache::lonenc::encrypted($self->symb());} + return $self->symb(); +} sub symb { my $self=shift; (my $first, my $second) = $self->{ID} =~ /(\d+).(\d+)/; @@ -3361,6 +3507,16 @@ sub title { return $self->navHash("title_", 1); } # considered private and undocumented sub to { my $self=shift; return $self->navHash("to_", 1); } +sub condition { + my $self=shift; + my $undercond=$self->navHash("undercond_", 1); + if (!defined($undercond)) { return 1; }; + my $condid=$self->navHash("condid_$undercond"); + if (!defined($condid)) { return 1; }; + my $condition=&Apache::lonnet::directcondval($condid); + return $condition; +} + sub compTitle { my $self = shift; my $title = $self->title(); @@ -3782,6 +3938,16 @@ Returns the number of parts of the probl for single part problems, returns 1. For multipart, it returns the number of parts in the problem, not including psuedo-part 0. +=item * B(): + +Returns the total number of responses in the problem a student can answer. + +=item * B(): + +Returns a hash whose keys are the response types. The values are the number +of times each response type is used. This is for the I problem, not +just a single part. + =item * B(): Returns true if the problem is multipart, false otherwise. Use this instead @@ -3828,6 +3994,26 @@ sub countParts { return scalar(@{$parts}); # + $delta; } +sub countResponses { + my $self = shift; + my $count; + foreach my $part (@{$self->parts()}) { + $count+= scalar($self->responseIds($part)); + } + return $count; +} + +sub responseTypes { + my $self = shift; + my %responses; + foreach my $part ($self->parts()) { + foreach my $responsetype ($self->responseType($part)) { + $responses{$responsetype}++ if (defined($responsetype)); + } + } + return %responses; +} + sub multipart { my $self = shift; return $self->countParts() > 1; @@ -3915,6 +4101,7 @@ sub extractParts { } + # These hashes probably do not need names that end with "Hash".... my %responseIdHash; my %responseTypeHash; @@ -3950,17 +4137,27 @@ sub extractParts { } } my $resorder = &Apache::lonnet::metadata($self->src(),'responseorder'); + # + # Reorder the arrays in the %responseIdHash and %responseTypeHash if ($resorder) { my @resorder=split(/,/,$resorder); foreach my $part (keys(%responseIdHash)) { - my %resids = map { ($_,1) } @{ $responseIdHash{$part} }; + my $i=0; + my %resids = map { ($_,$i++) } @{ $responseIdHash{$part} }; my @neworder; foreach my $possibleid (@resorder) { if (exists($resids{$possibleid})) { - push(@neworder,$possibleid); + push(@neworder,$resids{$possibleid}); } } - $responseIdHash{$part}=\@neworder; + my @ids; + my @type; + foreach my $element (@neworder) { + push (@ids,$responseIdHash{$part}->[$element]); + push (@type,$responseTypeHash{$part}->[$element]); + } + $responseIdHash{$part}=\@ids; + $responseTypeHash{$part}=\@type; } } $self->{RESPONSE_IDS} = \%responseIdHash; @@ -4156,6 +4353,7 @@ sub getCompletionStatus { # Left as separate if statements in case we ever do more with this if ($status eq 'correct_by_student') {return $self->CORRECT;} + if ($status eq 'correct_by_scantron') {return $self->CORRECT;} if ($status eq 'correct_by_override') {return $self->CORRECT_BY_OVERRIDE; } if ($status eq 'incorrect_attempted') {return $self->INCORRECT; } if ($status eq 'incorrect_by_override') {return $self->INCORRECT_BY_OVERRIDE; } @@ -4492,6 +4690,7 @@ sub getNext { my $to = $self->to(); foreach my $branch ( split(/,/, $to) ) { my $choice = $self->{NAV_MAP}->getById($branch); + if (!$choice->condition()) { next; } my $next = $choice->goesto(); $next = $self->{NAV_MAP}->getById($next); 500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.