--- rat/lonsequence.pm 2003/04/07 20:47:06 1.15 +++ rat/lonsequence.pm 2020/09/03 12:14:30 1.52 @@ -2,7 +2,7 @@ # # Sequence Handler # -# $Id: lonsequence.pm,v 1.15 2003/04/07 20:47:06 www Exp $ +# $Id: lonsequence.pm,v 1.52 2020/09/03 12:14:30 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -26,15 +26,8 @@ # # http://www.lon-capa.org/ # -# (Handler to resolve ambiguous file locations -# -# (TeX Content Handler -# -# 05/29/00,05/30,10/11 Gerd Kortemeyer) -# -# 10/11,10/12 Gerd Kortemeyer) -# -# 10/16 Gerd Kortemeyer + + package Apache::lonsequence; @@ -42,10 +35,16 @@ use strict; use Apache::lonnet; use Apache::Constants qw(:common :http REDIRECT); use GDBM_File; -use Apache::lonratedt; -use Apache::lonratsrv; -use Apache::lonpageflip; -use Apache::loncommon; +use LONCAPA::map(); +use LONCAPA::ltiutils; +use LONCAPA; +use Apache::lonpageflip(); +use Apache::loncommon(); +use Apache::groupsort(); +use Apache::lonlocal; +use Apache::lonnavmaps(); +use Apache::lonenc(); +use HTML::Entities(); my %selhash; my $successtied; @@ -53,10 +52,10 @@ my $successtied; # ----------------------------------------- Attempt to read from resource space sub attemptread { - my $fn=shift; + my ($fn,$unsorted)=@_; &Apache::lonnet::repcopy($fn); if (-e $fn) { - return &Apache::lonratedt::attemptread($fn); + return &LONCAPA::map::attemptread($fn,$unsorted); } else { return (); } @@ -66,7 +65,7 @@ sub mapread { my $fn=shift; &Apache::lonnet::repcopy($fn); if (-e $fn) { - return &Apache::lonratedt::mapread($fn,''); + return &LONCAPA::map::mapread($fn,''); } else { return (); } @@ -76,9 +75,11 @@ sub mapread { sub viewmap { my ($r,$url)=@_; - $r->print(''); - if ($ENV{'form.forceselect'}) { $r->print(< + + my $js; + if ($env{'form.forceselect'}) { + $js = (< function select_group() { window.location="/adm/groupsort?catalogmode=groupsec&mode=rat&acts="+document.forms.fileattr.acts.value; @@ -100,111 +101,85 @@ function queue(val) { ENDSCRIPT } - $r->print(&Apache::loncommon::bodytag('Map Contents').'

'.$url.'

'); + + $r->print(&Apache::loncommon::start_page('Map Contents',$js). + '

'.$url.'

'); # ------------------ This is trying to select. Provide buttons and tie %selhash - if ($ENV{'form.forceselect'}) { $r->print(< - - + if ($env{'form.forceselect'}) { $r->print(< + + ENDSELECT my $diropendb = - "/home/httpd/perl/tmp/$ENV{'user.domain'}\_$ENV{'user.name'}_groupsec.db"; + LONCAPA::tempdir() . + "$env{'user.domain'}\_$env{'user.name'}_sel_res.db"; if (tie(%selhash,'GDBM_File',$diropendb,&GDBM_WRCREAT(),0640)) { - if ($ENV{'form.launch'} eq '1') { + if ($env{'form.launch'} eq '1') { &start_fresh_session(); } $successtied=1; # - Evaluate actions from previous page (both cumulatively and chronologically) - if ($ENV{'form.catalogmode'} eq 'groupimport') { - my $acts=$ENV{'form.acts'}; - my @Acts=split(/b/,$acts); - my %ahash; - my %achash; - my $ac=0; - # some initial hashes for working with data - foreach (@Acts) { - my ($state,$ref)=split(/a/); - $ahash{$ref}=$state; - $achash{$ref}=$ac; - $ac++; - } - # sorting through the actions and changing the tied database hash - foreach (sort {$achash{$a}<=>$achash{$b}} (keys %ahash)) { - my $key=$_; - if ($ahash{$key} eq '1') { - $selhash{'store_'.$selhash{'pre_'.$key.'_link'}}= - $selhash{'pre_'.$key.'_title'}; - $selhash{'storectr_'.$selhash{'pre_'.$key.'_link'}}= - $selhash{'storectr'}+0; - $selhash{'storectr'}++; - } - if ($ahash{$key} eq '0') { - if ($selhash{'store_'.$selhash{'pre_'.$key.'_link'}}) { - delete $selhash{'store_'.$selhash{'pre_'.$key.'_link'}}; - } - } + if ($env{'form.catalogmode'} eq 'import') { + &Apache::groupsort::update_actions_hash(\%selhash); } - # deleting the previously cached listing - foreach (keys %selhash) { - if ($_ =~ /^pre_/ && $_ =~/link$/) { - my $key = $_; - $key =~ s/^pre_//; - $key =~ s/_[^_]*$//; - delete $selhash{'pre_'.$key.'_title'}; - delete $selhash{'pre_'.$key.'_link'}; - } - } - } # - } } # ----------------------------- successtied is now '1' if in working selectmode my ($errtext,$fatal)=&mapread(&Apache::lonnet::filelocation('',$url),''); if ($fatal==1) { - $r->print('

Map contents are not shown in order.


'); + $r->print('

' + .&mt('Map contents are not shown in order.') + .'


'); } my $idx=0; - foreach (&attemptread(&Apache::lonnet::filelocation('',$url))) { - if (defined($_)) { + foreach my $entry (&attemptread(&Apache::lonnet::filelocation('',$url))) { + if (defined($entry)) { $idx++; if ($successtied) { $r->print('
'); } - my ($title,$url)=split(/\:/,$_); - $title=~s/\&colon\;/\:/g; - $url=~s/\&colon\;/\:/g; - unless ($title) { $title=(split(/\//,$url))[-1] }; - unless ($title) { $title='Empty'; } + my ($title,$url)=split(/\:/,$entry); + $title = &LONCAPA::map::qtescape($title); + unless ($title) { $title=(split(/\//,$url))[-1] }; + my $enc_title = &HTML::Entities::encode($title,'\'"<>&'); + unless ($title) { + $title=''.&mt('Empty').''; + $enc_title = &mt('Empty'); + } + $url = &LONCAPA::map::qtescape($url); + my $enc_url = &HTML::Entities::encode($url,'\'"<>&'); if ($url) { if ($successtied) { my $checked=''; if ($selhash{'store_'.$url}) { - $checked=" checked"; + $checked=' checked="checked"'; } $selhash{"pre_${idx}_link"}=$url; $selhash{"pre_${idx}_title"}=$title; - + + $url = &HTML::Entities::encode($url, '\'"<>&'); $r->print(< - +value='$enc_url' onClick='javascript:queue("form$idx")'$checked /> + ENDCHECKBOX } - $r->print(''); + $r->print(''); } - $r->print(&Apache::lonratsrv::qtescape($title)); + $r->print($enc_title); if ($url) { $r->print(''); } if ($successtied) { $r->print(''); } else { - $r->print('
'); + $r->print('
'); } } } - $r->print(''); + $r->print(&Apache::loncommon::end_page()); if ($successtied) { untie %selhash; } @@ -212,12 +187,12 @@ ENDCHECKBOX # ----------------------------------------------------------- Clean out selhash sub start_fresh_session { - foreach (keys %selhash) { - if ($_ =~ /^pre_/) { - delete $selhash{$_}; + foreach my $item (keys(%selhash)) { + if ($item =~ /^pre_/) { + delete $selhash{$item}; } - if ($_ =~ /^store/) { - delete $selhash{$_}; + if ($item =~ /^store/) { + delete $selhash{$item}; } } } @@ -229,26 +204,151 @@ sub handler { my $r=shift; if ($r->header_only) { - $r->content_type('text/html'); + &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; return OK; } - + + my $requrl=$r->uri; &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, - ['forceselect','launch']); + ['forceselect','launch','navmap']); + + if (($env{'request.course.fn'}) && ($env{'form.navmap'}) && ($env{'request.course.id'})) { + my $crstype = &Apache::loncommon::course_type(); + unless (($crstype eq 'Placement') && (!$env{'request.role.adv'})) { + + # Check for critical messages and redirect if present. + my ($redirect,$url) = &Apache::loncommon::critical_redirect(300,'contents'); + if ($redirect) { + &Apache::loncommon::content_type($r,'text/html'); + $r->header_out(Location => $url); + return REDIRECT; + } + + # Check if course needs to be re-initialized + my $loncaparev = $r->dir_config('lonVersion'); + my ($result,@reinit) = &Apache::loncommon::needs_coursereinit($loncaparev); + + if ($result eq 'switch') { + &Apache::loncommon::content_type($r,'text/html'); + $r->send_http_header; + $r->print(&Apache::loncommon::check_release_result(@reinit)); + return OK; + } elsif ($result eq 'update') { + my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum"); + if ($ferr) { + my $requrl = $r->uri; + $env{'user.error.msg'}="$requrl:bre:0:0:Course not initialized"; + $env{'user.reinit'} = 1; + return HTTP_NOT_ACCEPTABLE; + } + } + + &Apache::loncommon::content_type($r,'text/html'); + $r->send_http_header; + + my $mapurl = &Apache::lonnet::declutter($requrl); + my $maptitle = &Apache::lonnet::gettitle($mapurl); + my @crumbs = ({text => $maptitle, no_mt => 1}); + my $args = {'bread_crumbs' => \@crumbs, + 'bread_crumbs_nomenu' => 1}; + + # Create the nav map + my $navmap = Apache::lonnavmaps::navmap->new(); + + if (ref($navmap)) { + # renderer call + if (&Apache::lonnet::is_on_map($requrl)) { + my ($ltiscope,$ltiuri); + if (($env{'request.lti.login'}) && ($env{'request.lti.uri'})) { + my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + ($ltiscope,$ltiuri) = &LONCAPA::ltiutils::lti_provider_scope($env{'request.lti.uri'},$cdom,$cnum); + } + @crumbs = (); + unless ($ltiscope eq 'resource') { + unless ($ltiscope eq 'map') { + @crumbs = ({text => $crstype.' Contents', + href => "javascript:gopost('/adm/navmaps','')"}); + } + my $res = $navmap->getResourceByUrl($mapurl); + if (ref($res)) { + my $symb = $res->symb(); + if ($symb) { + my ($parent) = &Apache::lonnet::decode_symb($res->symb()); + if (($parent ne $env{'course.'.$env{'request.course.id'}.'.url'}) && + !(($ltiscope eq 'map') && ($requrl eq $ltiuri))) { + my @mapcrumbs = $navmap->recursed_crumbs($parent); + if (@mapcrumbs) { + push(@crumbs,@mapcrumbs); + } + } + $env{'request.symb'} = $symb; + } + } + } + push(@crumbs,{text => $maptitle, no_mt => 1}); + $args = {'bread_crumbs' => \@crumbs, + 'bread_crumbs_nomenu' => 1}; + $r->print(&Apache::loncommon::start_page($maptitle,undef,$args)); + + my $renderArgs = { 'cols' => [0,1,2,3], + 'url' => $mapurl, + 'navmap' => $navmap, + 'suppressNavmap' => 1, + 'suppressEmptySequences' => 1, + 'filterFunc' => undef, + 'resource_no_folder_link' => 1, + 'r' => $r, + 'caller' => 'sequence', + 'notools' => 1, + 'iterator_map' => $mapurl, + }; + + my $render = &Apache::lonnavmaps::render($renderArgs); + + # If no resources were found let the user know. + if ($renderArgs->{'counter'} == 0) { + $r->print('

'. + &mt('No items found in folder'). + '

'); + } + $r->print(&Apache::loncommon::end_page()); + } else { + $r->print(&Apache::loncommon::start_page($maptitle,undef,$args). + '

'. + &mt('Folder no longer appears to be a part of the course'). + '

'. + &Apache::loncommon::end_page()); + } + } else { + $r->print(&Apache::loncommon::start_page($maptitle,undef,$args). + '

'. + &mt('Error: could not determine contents of folder'). + '

'. + &Apache::loncommon::end_page()); + } + $r->rflush(); + return OK; + } + } my %hash; my %bighash; - my $requrl=$r->uri; $successtied=0; # ------------------------------------------------------------ Tie symb db file my $disurl=''; my $dismapid=''; + my $exitdisid = ''; + my $arrow_dir = ''; + my $is_encrypted = ''; - if (($ENV{'request.course.fn'}) && (!$ENV{'form.forceselect'})) { + if (($env{'request.course.fn'}) && (!$env{'form.forceselect'})) { my $last; - if (tie(%hash,'GDBM_File',$ENV{'request.course.fn'}.'_symb.db', + if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db', &GDBM_READER(),0640)) { $last=$hash{'last_direction'}; untie(%hash); @@ -256,24 +356,37 @@ sub handler { my $direction=''; my $prevmap=''; if ($last) { - ($prevmap,$direction)=(split(/\_\_\_/,$last)); + ($prevmap,undef,$direction)=&Apache::lonnet::decode_symb($last); } # ------------------------------------------------------------- Tie big db file - if (tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db', + if (tie(%bighash,'GDBM_File',$env{'request.course.fn'}.'.db', &GDBM_READER(),0640)) { my $disid=''; + my $randomout =''; if ($direction eq 'back') { $disid=$bighash{'map_finish_'.$requrl}; } else { $disid=$bighash{'map_start_'.$requrl}; - } + } if ($disid) { $disurl=$bighash{'src_'.$disid}; $dismapid=(split(/\./,$disid))[1]; + if (!$env{'request.role.adv'}) { + $randomout = $bighash{'randomout_'.$disid}; + } + if (!$env{'request.role.adv'}) { + $is_encrypted = $bighash{'encrypted_'.$disid}; + } + } elsif (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db', + &GDBM_READER(),0640)) { + $last=$hash{'last_known'}; + untie(%hash); } -# ------------------------- If this is an empty one, skip to next non-empty one - if ((!$disurl) && ($disid)) { + + +# ----------- If this is an empty one, or hidden, skip to next non-empty or non-hidden one + while ( ((!$disurl) && ($disid)) || ($randomout && $disid) ) { $direction=($direction?$direction:'forward'); ($disid,$requrl)= &Apache::lonpageflip::fullmove($disid, @@ -281,8 +394,16 @@ sub handler { if ($disid) { $disurl=$bighash{'src_'.$disid}; $dismapid=(split(/\./,$disid))[1]; + if (!$env{'request.role.adv'}) { + $randomout = $bighash{'randomout_'.$disid}; + } + if (!$env{'request.role.adv'}) { + $is_encrypted = $bighash{'encrypted_'.$disid}; + } } } + $exitdisid = $disid; + $arrow_dir = $direction; # --------------------------------------- Untie hash, make sure to come by here untie(%bighash); @@ -290,18 +411,61 @@ sub handler { } # now either disurl is set (going to first page), or we need another display - if ($disurl) { # -------------------------------------------------- Has first or last resource - &Apache::lonnet::symblist($requrl,$disurl => $dismapid, - 'last_known' => &Apache::lonnet::declutter($disurl)); - $r->content_type('text/html'); - $r->header_out(Location => 'http://'.$ENV{'HTTP_HOST'}.$disurl); + my $showdisurl = $disurl; + if ($is_encrypted) { + $showdisurl = &Apache::lonenc::encrypted($disurl); + } + if ($disurl =~ m{/adm/navmaps(\?|$)}) { + &Apache::lonnet::symblist($requrl,$disurl => [$disurl,$dismapid]); + } else { + &Apache::lonnet::symblist($requrl,$disurl => [$disurl,$dismapid], + 'last_known' => [$disurl,$dismapid]); + } + &Apache::loncommon::content_type($r,'text/html'); + $r->header_out(Location => &Apache::lonnet::absolute_url($ENV{'SERVER_NAME'}). + $showdisurl); return REDIRECT; } else { - $r->content_type('text/html'); + &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; - &viewmap($r,$requrl); + if ($exitdisid eq '' && $arrow_dir ne '') { + my %lt =&Apache::lonlocal::texthash( + 'nere' => 'Next resource could not be displayed', + 'goba' => 'Go Back', + 'nacc' => 'Course Contents', + ); + if (&Apache::loncommon::course_type() eq 'Community') { + $lt{'nav'} = &mt('Community Contents'); + } + my $warnmsg; + if ($arrow_dir eq 'forward') { + $warnmsg = &mt('As all folders and sequences ' + .'following the current resource were empty, ' + .'you have now reached the end of the course.'); + } elsif ($arrow_dir eq 'back') { + $warnmsg = &mt('As all folders and sequences ' + .'preceding the current resource were empty, ' + .'you have now reached the beginning of the course.'); + } + my $start_page= + &Apache::loncommon::start_page('Empty Folder/Sequence'); + my $end_page= + &Apache::loncommon::end_page(); + $r->print(<$lt{'nere'} +

$warnmsg

+ +$end_page +ENDNONE + } else { + &viewmap($r,$requrl); + } return OK; } } @@ -309,7 +473,35 @@ sub handler { 1; __END__ +=head1 NAME + +Apache::lonsequence + +=head1 SYNOPSIS + +Handler for showing sequence objects of +educational resources. + +This is part of the LearningOnline Network with CAPA project +described at http://www.lon-capa.org. + +=head1 SUBROUTINES + +=over + +=item handler() + +=item viewmap() + +=item attemptread() + +=item mapread() + +=item start_fresh_session() + +=back +=cut