# The LearningOnline Network with CAPA # # Sequence Handler # # $Id: lonsequence.pm,v 1.52 2020/09/03 12:14:30 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LON-CAPA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ # package Apache::lonsequence; use strict; use Apache::lonnet; use Apache::Constants qw(:common :http REDIRECT); use GDBM_File; 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; # ----------------------------------------- Attempt to read from resource space sub attemptread { my ($fn,$unsorted)=@_; &Apache::lonnet::repcopy($fn); if (-e $fn) { return &LONCAPA::map::attemptread($fn,$unsorted); } else { return (); } } sub mapread { my $fn=shift; &Apache::lonnet::repcopy($fn); if (-e $fn) { return &LONCAPA::map::mapread($fn,''); } else { return (); } } # ---------------------------------------------------------------- View Handler sub viewmap { my ($r,$url)=@_; my $js; if ($env{'form.forceselect'}) { $js = (< function select_group() { window.location="/adm/groupsort?catalogmode=groupsec&mode=rat&acts="+document.forms.fileattr.acts.value; } function queue(val) { if (eval("document.forms."+val+".filelink.checked")) { var l=val.length; var v=val.substring(4,l); document.forms.fileattr.acts.value+='1a'+v+'b'; } else { var l=val.length; var v=val.substring(4,l); document.forms.fileattr.acts.value+='0a'+v+'b'; } } ENDSCRIPT } $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(< ENDSELECT my $diropendb = 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') { &start_fresh_session(); } $successtied=1; # - Evaluate actions from previous page (both cumulatively and chronologically) if ($env{'form.catalogmode'} eq 'import') { &Apache::groupsort::update_actions_hash(\%selhash); } # - } } # ----------------------------- successtied is now '1' if in working selectmode my ($errtext,$fatal)=&mapread(&Apache::lonnet::filelocation('',$url),''); if ($fatal==1) { $r->print('

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


'); } my $idx=0; foreach my $entry (&attemptread(&Apache::lonnet::filelocation('',$url))) { if (defined($entry)) { $idx++; if ($successtied) { $r->print('
'); } 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"'; } $selhash{"pre_${idx}_link"}=$url; $selhash{"pre_${idx}_title"}=$title; $url = &HTML::Entities::encode($url, '\'"<>&'); $r->print(< ENDCHECKBOX } $r->print(''); } $r->print($enc_title); if ($url) { $r->print(''); } if ($successtied) { $r->print(''); } else { $r->print('
'); } } } $r->print(&Apache::loncommon::end_page()); if ($successtied) { untie %selhash; } } # ----------------------------------------------------------- Clean out selhash sub start_fresh_session { foreach my $item (keys(%selhash)) { if ($item =~ /^pre_/) { delete $selhash{$item}; } if ($item =~ /^store/) { delete $selhash{$item}; } } } # ================================================================ Main Handler sub handler { my $r=shift; if ($r->header_only) { &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','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; $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'})) { my $last; if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db', &GDBM_READER(),0640)) { $last=$hash{'last_direction'}; untie(%hash); } my $direction=''; my $prevmap=''; if ($last) { ($prevmap,undef,$direction)=&Apache::lonnet::decode_symb($last); } # ------------------------------------------------------------- Tie big db file 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, 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, &Apache::lonnet::declutter($requrl),$direction); 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); } } # now either disurl is set (going to first page), or we need another display if ($disurl) { # -------------------------------------------------- Has first or last resource 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 { &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; 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; } } 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