# The LearningOnline Network with CAPA # # Page flip handler # # $Id: lonpageflip.pm,v 1.106 2021/07/19 15:48:25 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::lonpageflip; use strict; use LONCAPA; use Apache::Constants qw(:common :http REDIRECT); use Apache::lonnet; use Apache::loncommon(); use Apache::lonnavmaps(); use Apache::lonuserstate; use Apache::lonlocal; use HTML::TokeParser; use GDBM_File; # ========================================================== Module Global Hash my %hash; sub cleanup { if (tied(%hash)){ &Apache::lonnet::logthis('Cleanup pageflip: hash'); unless (untie(%hash)) { &Apache::lonnet::logthis('Failed cleanup pageflip: hash'); } } return OK; } sub addrid { my ($current,$new,$condid)=@_; unless ($condid) { $condid=0; } if ($current) { $current.=','.$new; } else { $current=''.$new; } return $current; } sub fullmove { my ($rid,$mapurl,$direction)=@_; if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'.db', &GDBM_READER(),0640)) { ($rid,$mapurl)=&move($rid,$mapurl,$direction); untie(%hash); } return($rid,$mapurl); } sub hash_src { my ($id)=@_; my ($mapid,$resid)=split(/\./,$id); my $symb=&Apache::lonnet::encode_symb($hash{'map_id_'.$mapid}, $resid,$hash{'src_'.$id}); my $anchor; if ($hash{'ext_'.$id} eq 'true:') { if ($hash{'src_'.$id} =~ /(\#.+)$/) { $anchor = $1; } } if ($hash{'encrypted_'.$id}) { return (&Apache::lonenc::encrypted($hash{'src_'.$id}), &Apache::lonenc::encrypted($symb), $hash{'encrypted_'.$id},$anchor); } return ($hash{'src_'.$id},$symb,$hash{'encrypted_'.$id},$anchor); } sub move { my ($next,$endupmap,$direction,$firstres) = @_; my $safecount=0; my $allowed=0; my $deeplinkonly=0; my $deeplinkchecked; my $deeplink_login_pc; my $prev=$next; my ($prevmapid)=split(/\./,$next); do { ($next,$endupmap)=&get_next_possible_move($next,$endupmap,$direction); my $url = $hash{'src_'.$next}; my ($mapid,$resid)=split(/\./,$next); my $symb = &Apache::lonnet::encode_symb($hash{'map_id_'.$mapid}, $resid,$url); if ($url eq '' || $symb eq '') { $allowed = 0; } else { my $nodeeplinkcheck = 0; if ($hash{'is_map_'.$next}) { $nodeeplinkcheck = 1; } my $priv = &Apache::lonnet::allowed('bre',$url,$symb,'','','','',$nodeeplinkcheck); $allowed = (($priv eq 'F') || ($priv eq '2') || ($priv eq 'A')); } $deeplinkonly = 0; if ($hash{'deeplinkonly_'.$next}) { my ($value,$level) = split(/:/,$hash{'deeplinkonly_'.$next}); my ($listed,$scope,$access) = split(/,/,$value); unless (($access eq 'any') || ($hash{'is_map_'.$next})) { if ($level eq 'resource') { $deeplinkonly = 1; } elsif ($level eq 'map') { if ($scope eq 'rec') { unless ($mapid == $prevmapid) { unless ($deeplinkchecked) { $deeplink_login_pc = &get_deeplink_login_pc(); $deeplinkchecked = 1; } if ($deeplink_login_pc) { my $poss_map_pc; if ($hash{'is_map_'.$next}) { $poss_map_pc = $hash{'map_pc_'.$url}; } else { $poss_map_pc = $hash{'map_pc_'.$hash{'map_id_'.$mapid}}; } unless ($deeplink_login_pc == $poss_map_pc) { unless (grep(/^$deeplink_login_pc$/,split(/,/,$hash{'map_hierarchy_'.$poss_map_pc}))) { $deeplinkonly = 1; } } } else { $deeplinkonly = 1; } } } elsif ($mapid != $prevmapid) { $deeplinkonly = 1; } } } } elsif (($hash{'deeplinkonly_'.$prev}) && (!$firstres)) { my ($value,$level) = split(/:/,$hash{'deeplinkonly_'.$prev}); my ($listed,$scope,$access) = split(/,/,$value); unless (($access eq 'any') || ($hash{'is_map_'.$prev})) { if ($level eq 'resource') { $deeplinkonly = 1; } elsif ($level eq 'map') { my ($listed,$scope,$access) = split(/,/,$value); if ($scope eq 'rec') { unless ($mapid == $prevmapid) { unless ($deeplinkchecked) { $deeplink_login_pc = &get_deeplink_login_pc(); $deeplinkchecked = 1; } if ($deeplink_login_pc) { my $poss_map_pc; if ($hash{'is_map_'.$prev}) { $poss_map_pc = $hash{'map_pc_'.$url}; } else { $poss_map_pc = $hash{'map_pc_'.$hash{'map_id_'.$mapid}}; } unless ($deeplink_login_pc == $poss_map_pc) { unless (grep(/^$deeplink_login_pc$/,split(/,/,$hash{'map_hierarchy_'.$poss_map_pc}))) { $deeplinkonly = 1; } } } } } else { if ($mapid != $prevmapid) { $deeplinkonly = 1; } } } } } $safecount++; } while ( ($next) && ($next!~/\,/) && ( (!$hash{'src_'.$next}) || ( (!$env{'request.role.adv'}) && (($hash{'randomout_'.$next}) || ($deeplinkonly) || ($hash{'deeplinkout_'.$next}) ) || (!$allowed) ) && ($safecount<10000)); return ($next,$endupmap); } sub get_next_possible_move { my ($rid,$mapurl,$direction)=@_; my $startoutrid=$rid; my $next=''; my $mincond=1; my $posnext=''; if ($direction eq 'forward') { # --------------------------------------------------------------------- Forward while ($hash{'type_'.$rid} eq 'finish') { $rid=$hash{'ids_'.$hash{'map_id_'.(split(/\./,$rid))[0]}}; } foreach my $id (split(/\,/,$hash{'to_'.$rid})) { my $condition= $hash{'conditions_'.$hash{'goesto_'.$id}}; my $rescond = &Apache::lonnet::docondval($condition); my $linkcond = &Apache::lonnet::directcondval($hash{'condid_'.$hash{'undercond_'.$id}}); my $thiscond = ($rescond<$linkcond)?$rescond:$linkcond; if ($thiscond>=$mincond) { if ($posnext) { $posnext.=','.$id.':'.$thiscond; } else { $posnext=$id.':'.$thiscond; } if ($thiscond>$mincond) { $mincond=$thiscond; } } } foreach my $id (split(/\,/,$posnext)) { my ($linkid,$condval)=split(/\:/,$id); if ($condval>=$mincond) { $next=&addrid($next,$hash{'goesto_'.$linkid}, $hash{'condid_'.$hash{'undercond_'.$linkid}}); } } if ($hash{'is_map_'.$next}) { # This jumps to the beginning of a new map (going down level) if ( $hash{'map_type_'.$hash{'map_pc_'.$hash{'src_'.$next}}} eq 'sequence') { $mapurl=$hash{'src_'.$next}; $next=$hash{'map_start_'.$hash{'src_'.$next}}; } elsif ( # This jumps back up from an empty sequence, to a page up one level $hash{'map_type_'.$hash{'map_pc_'.$hash{'src_'.$next}}} eq 'page') { $mapurl=$hash{'map_id_'.(split(/\./,$next))[0]}; } } elsif ((split(/\./,$startoutrid))[0]!=(split(/\./,$next))[0]) { # This comes up from a map (coming up one level); $mapurl=$hash{'map_id_'.(split(/\./,$next))[0]}; } } elsif ($direction eq 'back') { # ------------------------------------------------------------------- Backwards while ($hash{'type_'.$rid} eq 'start') { $rid=$hash{'ids_'.$hash{'map_id_'.(split(/\./,$rid))[0]}}; } foreach my $id (split(/\,/,$hash{'from_'.$rid})) { my $condition= $hash{'conditions_'.$hash{'comesfrom_'.$id}}; my $rescond = &Apache::lonnet::docondval($condition); my $linkcond = &Apache::lonnet::directcondval($hash{'condid_'.$hash{'undercond_'.$id}}); my $thiscond = ($rescond<$linkcond)?$rescond:$linkcond; if ($thiscond>=$mincond) { if ($posnext) { $posnext.=','.$id.':'.$thiscond; } else { $posnext=$id.':'.$thiscond; } if ($thiscond>$mincond) { $mincond=$thiscond; } } } foreach my $id (split(/\,/,$posnext)) { my ($linkid,$condval)=split(/\:/,$id); if ($condval>=$mincond) { $next=&addrid($next,$hash{'comesfrom_'.$linkid}, $hash{'condid_'.$hash{'undercond_'.$linkid}}); } } if ($hash{'is_map_'.$next}) { # This jumps to the end of a new map (going down one level) if ( $hash{'map_type_'.$hash{'map_pc_'.$hash{'src_'.$next}}} eq 'sequence') { $mapurl=$hash{'src_'.$next}; $next=$hash{'map_finish_'.$hash{'src_'.$next}}; } elsif ( $hash{'map_type_'.$hash{'map_pc_'.$hash{'src_'.$next}}} eq 'page') { # This jumps back up from an empty sequence, to a page up one level $mapurl=$hash{'map_id_'.(split(/\./,$next))[0]}; } } elsif ((split(/\./,$startoutrid))[0]!=(split(/\./,$next))[0]) { # This comes back up from a map (going up one level); $mapurl=$hash{'map_id_'.(split(/\./,$next))[0]}; } } return ($next,$mapurl); } sub first_accessible_resource { my $furl; if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'.db', &GDBM_READER(),0640)) { $furl=$hash{'first_url'}; my (%args,$url,$argstr); if ($furl =~ m{^/enc/}) { ($url,$argstr) = split(/\?/,&Apache::lonenc::unencrypted($furl)); } else { ($url,$argstr) = split(/\?/,$furl); } foreach my $pair (split(/\&/,$argstr)) { my ($name,$value) = split(/=/,$pair); $args{&unescape($name)} = &unescape($value); } my $priv = &Apache::lonnet::allowed('bre',$url,$args{'symb'}); my $allowed = (($priv eq 'F') || ($priv eq '2') || ($priv eq 'A')); if (!$allowed) { # Wow, we cannot see this ... move forward to the next one that we can see my ($newrid,$newmap)=&move($hash{'first_rid'},$hash{'first_mapurl'},'forward',1); # Build the new URL if ($newrid eq '') { $furl = '/adm/navmaps'; } else { my ($newmapid,$newresid)=split(/\./,$newrid); my $symb=&Apache::lonnet::encode_symb($newmap,$newresid,$hash{'src_'.$newrid}); $furl=&add_get_param($hash{'src_'.$newrid},{ 'symb' => $symb }); if ($hash{'encrypted_'.$newrid}) { $furl=&Apache::lonenc::encrypted($furl); } { } untie(%hash); return $furl; } else { return '/adm/navmaps'; } } sub first_answerable_ressymb { my $navmap = Apache::lonnavmaps::navmap->new; return unless (ref($navmap)); my $iterator = $navmap->getIterator(undef,undef,undef,1); return unless (ref($iterator)); my ($curRes,$result); while ($curRes = $iterator->next()) { if (ref($curRes) && $curRes->is_problem()) { foreach my $part (@{$curRes->parts()}) { if ($curRes->tries($part) < $curRes->maxtries($part)) { $result = $curRes->link().'?symb='.$curRes->shown_symb(); last; } } } } if ($result) { return $result; } else { return &first_accessible_resource(); } } sub check_http_req { my ($srcref,$hostname) = @_; return unless (ref($srcref) eq 'SCALAR'); my $usehttp; if ($env{'request.course.id'}) { my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; if (($$srcref =~ m{^\Q/public/$cdom/$cnum/syllabus\E($|\?)}) && ($ENV{'SERVER_PORT'} == 443) && ($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'} =~ m{^http://})) { unless ((&Apache::lonnet::uses_sts()) || (&Apache::lonnet::waf_allssl($hostname))) { $$srcref .= (($$srcref =~/\?/)? '&':'?') . 'usehttp=1'; $usehttp = 1; } } elsif (($$srcref =~ m{^\Q/adm/wrapper/ext/\E(?!https:)}) && ($ENV{'SERVER_PORT'} == 443)) { unless ((&Apache::lonnet::uses_sts()) || (&Apache::lonnet::waf_allssl($hostname))) { my ($url,$anchor) = ($$srcref =~ /^([^\#]+)(?:|(\#[^\#]+))$/); $$srcref = $url . (($$srcref =~/\?/)? '&':'?') . 'usehttp=1' .$anchor; $usehttp = 1; } } } return $usehttp; } sub reinited_js { my ($url,$cid,$timeout) = @_; if (!$timeout) { $timeout = 0; } return <<"END"; END } sub get_deeplink_login_pc { my $deeplink_login_pc; if (($env{'request.deeplink.login'}) && ($env{'request.course.id'})) { my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; if ($env{'request.deeplink.login'}) { my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $deeplink_symb = &Apache::loncommon::deeplink_login_symb($cnum,$cdom); if ($deeplink_symb) { my $loginmap; if ($deeplink_symb =~ /\.(page|sequence)$/) { $loginmap = &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($deeplink_symb))[2]); } else { $loginmap = &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($deeplink_symb))[0]); } $deeplink_login_pc = $hash{'map_pc_'.$loginmap}; } } } return $deeplink_login_pc; } # ================================================================ Main Handler sub handler { my $r=shift; # ------------------------------------------- Set document type for header only if ($r->header_only) { &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; return OK; } my %cachehash=(); my $multichoice=0; my %multichoicehash=(); my %prog_state=(); my ($redirecturl,$redirectsymb,$enc,$anchor,$deeplinklevel); my $next=''; my $hostname = $r->hostname(); my @possibilities=(); &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['postdata']); if (($env{'form.postdata'})&&($env{'request.course.fn'})) { my ($direction,$currenturl) = ($env{'form.postdata'}=~/(\w+)\:(.*)/); if ($currenturl=~m|^/enc/|) { $currenturl=&Apache::lonenc::unencrypted($currenturl); } $currenturl=~s/\.\d+\.(\w+)$/\.$1/; $currenturl=~s/^https?\:\/\///; $currenturl=~s/^[^\/]+//; my ($preupdatepos,$last,$reinitcheck); if ($direction eq 'return') { if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db', &GDBM_READER(),0640)) { $last=$hash{'last_known'}; untie(%hash); } } elsif ($direction eq 'firstanswerable') { my $furl = &first_answerable_ressymb(); my $usehttp = &check_http_req(\$furl,$hostname); if (($usehttp) && ($hostname ne '')) { $furl='http://'.$hostname.$furl; } else { $furl=&Apache::lonnet::absolute_url().$furl; } &Apache::loncommon::content_type($r,'text/html'); $r->header_out(Location => $furl); return REDIRECT; } elsif ($direction eq 'endplacement') { &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; $r->print(&Apache::lonplacementtest::showresult()); return OK; } if ($env{'request.course.id'}) { # Check if course needs to be re-initialized my $loncaparev = $r->dir_config('lonVersion'); ($reinitcheck,my @reinit) = &Apache::loncommon::needs_coursereinit($loncaparev); if ($reinitcheck eq 'switch') { &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; $r->print(&Apache::loncommon::check_release_result(@reinit)); return OK; } elsif ($reinitcheck eq 'update') { my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; $preupdatepos = &Apache::lonnet::symbread($currenturl); unless ($direction eq 'return') { if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db', &GDBM_READER(),0640)) { $last=$hash{'last_known'}; untie(%hash); } } &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; $r->print(&Apache::loncommon::start_page('Content Changed')); my $preamble = '
'. '
'. &mt('Your course session is being updated because of recent changes by course personnel.'). ' '.&mt('Please be patient').'.
'. '
'; %prog_state = &Apache::lonhtmlcommon::Create_PrgWin($r,undef,$preamble); &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,&mt('Updating course')); my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum"); &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,&mt('Finished!')); if ($ferr) { &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); my $requrl = $r->uri; $env{'user.error.msg'}="$requrl:bre:0:0:Course not initialized"; $env{'user.reinit'} = 1; return HTTP_NOT_ACCEPTABLE; } else { if ($last) { my ($murl,$id,$fn)=&Apache::lonnet::decode_symb($last); unless (&Apache::lonnet::symbverify($last,$fn)) { undef($last); } } } } } if ($direction eq 'firstres') { my $furl=&first_accessible_resource(); my $usehttp = &check_http_req(\$furl,$hostname); if (($usehttp) && ($hostname ne '')) { $furl='http://'.$hostname.$furl; } else { $furl=&Apache::lonnet::absolute_url().$furl; } if ($reinitcheck eq 'update') { &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); $r->print(&reinited_js($furl,$env{'request.course.id'},100)); $r->print(&Apache::loncommon::end_page()); return OK; } else { &Apache::loncommon::content_type($r,'text/html'); $r->header_out(Location => $furl); return REDIRECT; } } if ($direction eq 'return') { # -------------------------------------------------------- Return to last known my ($newloc,$usehttp); if (($last) && (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'.db', &GDBM_READER(),0640))) { my ($murl,$id,$fn)=&Apache::lonnet::decode_symb($last); $id=$hash{'map_pc_'.&Apache::lonnet::clutter($murl)}.'.'.$id; $newloc=$hash{'src_'.$id}; if ($newloc) { $usehttp = &check_http_req(\$newloc,$hostname); if ($hash{'encrypted_'.$id}) { $newloc=&Apache::lonenc::encrypted($newloc); } if ($newloc =~ m{^(/adm/wrapper/ext/[^\#]+)(?:|(\#[^\#]+))$}) { my ($url,$anchor) = ($1,$2); if ($anchor) { $newloc = $url.(($url=~/\?/)?'&':'?').'symb='.&escape($last).$anchor; } } } else { $newloc='/adm/navmaps'; } untie %hash; } else { $newloc='/adm/navmaps'; } if (($usehttp) && ($hostname ne '')) { $newloc='http://'.$hostname.$newloc; } else { $newloc=&Apache::lonnet::absolute_url().$newloc } if ($reinitcheck eq 'update') { $r->print(&reinited_js($newloc,$env{'request.course.id'},100)); $r->print(&Apache::loncommon::end_page()); return OK; } else { &Apache::loncommon::content_type($r,'text/html'); $r->header_out(Location => $newloc); return REDIRECT; } } # # Is the current URL on the map? If not, start with last known URL # unless (&Apache::lonnet::is_on_map($currenturl)) { if ($preupdatepos) { undef($preupdatepos); } elsif (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db', &GDBM_READER(),0640)) { $last=$hash{'last_known'}; untie(%hash); } if ($last) { $currenturl=&Apache::lonnet::clutter((&Apache::lonnet::decode_symb($last))[2]); } else { my $newloc = &Apache::lonnet::absolute_url(). '/adm/navmaps'; if ($reinitcheck eq 'update') { &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); $r->print(&reinited_js($newloc,$env{'request.course.id'},100)); $r->print(&Apache::loncommon::end_page()); return OK; } else { &Apache::loncommon::content_type($r,'text/html'); $r->header_out(Location => $newloc); return REDIRECT; } } } # ------------------------------------------- Do we have any idea where we are? my $position; if ($preupdatepos) { $position = $preupdatepos; } else { $position=Apache::lonnet::symbread($currenturl); } if ($position) { # ------------------------------------------------------------------------- Yes my ($startoutmap,$mapnum,$thisurl)=&Apache::lonnet::decode_symb($position); $cachehash{$startoutmap}{$thisurl}=[$thisurl,$mapnum]; $cachehash{$startoutmap}{'last_known'}= [&Apache::lonnet::declutter($currenturl),$mapnum]; # ============================================================ Tie the big hash if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'.db', &GDBM_READER(),0640)) { my $rid=$hash{'map_pc_'.&Apache::lonnet::clutter($startoutmap)}. '.'.$mapnum; # ------------------------------------------------- Move forward, backward, etc my $endupmap; ($next,$endupmap)=&move($rid,$startoutmap,$direction); # -------------------------------------- Do we have one and only one empty URL? # We are now at at least one non-empty URL # ----------------------------------------------------- Check out possibilities if ($next) { @possibilities=split(/\,/,$next); if ($#possibilities==0) { # ---------------------------------------------- Only one possibility, redirect ($redirecturl,$redirectsymb,$enc,$anchor)=&hash_src($next); $cachehash{$endupmap}{$redirecturl}= [$redirecturl,(split(/\./,$next))[1]]; } else { # ------------------------ There are multiple possibilities for a next resource $multichoice=1; foreach my $id (@possibilities) { $multichoicehash{'src_'.$id}=$hash{'src_'.$id}; $multichoicehash{'title_'.$id}=$hash{'title_'.$id}; $multichoicehash{'type_'.$id}=$hash{'type_'.$id}; (my $first, my $second) = $id =~ /(\d+).(\d+)/; my $symbSrc = Apache::lonnet::declutter($hash{'src_'.$id}); $multichoicehash{'symb_'.$id} = Apache::lonnet::declutter($hash{'map_id_'.$first}.'___'. $second.'___'.$symbSrc); my ($choicemap,$choiceres)=split(/\./,$id); my $map=&Apache::lonnet::declutter($hash{'src_'.$choicemap}); my $url=$multichoicehash{'src_'.$id}; $cachehash{$map}{$url}=[$url,$choiceres]; } } } else { # -------------------------------------------------------------- No place to go $multichoice=-1; if ($position && $env{'request.deeplink.login'}) { my ($map,$resid,$url) = &Apache::lonnet::decode_symb($position); my $mapid = $hash{'map_pc_'.&Apache::lonnet::clutter($map)}; my $position_deeplink = $hash{'deeplinkonly_'.$mapid.'.'.$resid}; if ($position_deeplink) { (my $value,$deeplinklevel) = split(/:/,$position_deeplink); } } } # ----------------- The program must come past this point to untie the big hash untie(%hash); # --------------------------------------------------------- Store position info $cachehash{$startoutmap}{'last_direction'}=[$direction,'notasymb']; foreach my $thismap (keys(%cachehash)) { my $mapnum=$cachehash{$thismap}->{'mapnum'}; delete($cachehash{$thismap}->{'mapnum'}); &Apache::lonnet::symblist($thismap, %{$cachehash{$thismap}}); } # ============================================== Do not return before this line if ($redirecturl) { # ----------------------------------------------------- There is a URL to go to if ($direction eq 'forward') { &Apache::lonnet::linklog($currenturl,$redirecturl); } if ($direction eq 'back') { &Apache::lonnet::linklog($redirecturl,$currenturl); } # ------------------------------------- Check for and display critical messages my ($redirect, $url) = &Apache::loncommon::critical_redirect(300,'flip'); unless ($redirect) { my $usehttp = &check_http_req(\$redirecturl,$hostname); if (($usehttp) && ($hostname ne '')) { $url='http://'.$hostname.$redirecturl; } else { $url=&Apache::lonnet::absolute_url().$redirecturl; } my $addanchor; if (($anchor ne '') && (!$enc || $env{'request.role.adv'})) { $addanchor = 1; $url =~ s/\#.+$//; } $url = &add_get_param($url, { 'symb' => $redirectsymb}); if ($addanchor) { $url .= $anchor; } } if ($reinitcheck eq 'update') { &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); $r->print(&reinited_js($url,$env{'request.course.id'},100)); $r->print(&Apache::loncommon::end_page()); return OK; } else { &Apache::loncommon::content_type($r,'text/html'); $r->header_out(Location => $url); return REDIRECT; } } else { # --------------------------------------------------------- There was a problem &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; my %lt=&Apache::lonlocal::texthash('title' => 'End of Sequence', 'deeplink' => 'No link available', 'deeplinkres' => 'Navigation to other content is unavailable when accessing content via deep-linking', 'deeplinkmap' => 'You have reached the end of the sequence of available materials for access via deep-linking', 'explain' => 'You have reached the end of the sequence of materials.', 'back' => 'Go Back', 'nav' => 'Course Contents', 'wherenext' => 'There are several possibilities of where to go next', 'pick' => 'Please click on the the resource you intend to access', 'titleheader' => 'Title', 'type' => 'Type', 'update' => 'Content updated', 'expupdate' => 'As a result of a recent update to the sequence of materials, it is not possible to complete the page flip.', 'gonav' => 'Go to the Contents page to select a resource to display.', ); if (&Apache::loncommon::course_type() eq 'Community') { $lt{'nav'} = &mt('Community Contents'); } if ($#possibilities>0) { my $start_page= &Apache::loncommon::start_page('Multiple Resources'); $r->print(<$lt{'wherenext'}

$lt{'pick'}:

ENDSTART foreach my $id (@possibilities) { my $src = $multichoicehash{'src_'.$id}; my $usehttp = &check_http_req(\$src,$hostname); if (($usehttp) && ($hostname ne '')) { $src = 'http://'.$hostname.$src; } $r->print( ''); } $r->print('
$lt{'titleheader'}$lt{'type'}
'. $multichoicehash{'title_'.$id}. ''.$multichoicehash{'type_'.$id}. '
'); } else { if ($reinitcheck) { if (&Apache::loncommon::course_type() eq 'Community') { $r->print( &Apache::loncommon::start_page('Community Contents Updated')); } else { $r->print( &Apache::loncommon::start_page('Course Contents Updated')); } $r->print('

'.$lt{'update'}.'

' .'

'.$lt{'expupdate'}.'
' .$lt{'gonav'}.'

'); } else { if (($env{'course.'.$env{'request.course.id'}.'.type'} eq 'Placement') && (!$env{'request.role.adv'})) { my ($score,$incomplete) = &Apache::lonplacementtest::check_completion(undef,undef,1); if ($incomplete) { $r->print(&Apache::lonplacementtest::showincomplete($incomplete)); } else { $r->print(&Apache::lonplacementtest::showresult(1)); } } else { $r->print( &Apache::loncommon::start_page('No Resource')); if ($deeplinklevel eq 'resource') { $r->print('

'.$lt{'deeplink'}.'

' .'

'.$lt{'deeplinkres'}.'

'); } elsif ($deeplinklevel eq 'map') { $r->print('

'.$lt{'title'}.'

' .'

'.$lt{'deeplinkmap'}.'

'); } else { $r->print('

'.$lt{'title'}.'

' .'

'.$lt{'explain'}.'

'); } } } } unless (($env{'course.'.$env{'request.course.id'}.'.type'} eq 'Placement') || ($env{'request.role.adv'})) { if ($deeplinklevel) { $r->print( &Apache::lonhtmlcommon::actionbox( [''.$lt{'back'}.''])); } elsif ((!@possibilities) && ($reinitcheck)) { $r->print( &Apache::lonhtmlcommon::actionbox( [''.$lt{'nav'}.'' ])); } else { $r->print( &Apache::lonhtmlcommon::actionbox( [''.$lt{'back'}.'', ''.$lt{'nav'}.'' ])); } } $r->print(&Apache::loncommon::end_page()); return OK; } } else { # ------------------------------------------------- Problem, could not tie hash if ($reinitcheck eq 'update') { &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); $r->print(&Apache::loncommon::end_page()); } $env{'user.error.msg'}="/adm/flip:bre:0:1:Course Data Missing"; return HTTP_NOT_ACCEPTABLE; } } else { # ---------------------------------------- No, could not determine where we are my $newloc = '/adm/ambiguous'; if ($reinitcheck eq 'update') { &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); $r->print(&reinited_js($newloc,$env{'request.course.id'},100)); $r->print(&Apache::loncommon::end_page()); } else { $r->internal_redirect($newloc); } return OK; } } else { # -------------------------- Class was not initialized or page fliped strangely $env{'user.error.msg'}="/adm/flip:bre:0:0:Choose Course"; return HTTP_NOT_ACCEPTABLE; } } 1; __END__ =pod =head1 NAME Apache::lonpageflip =head1 SYNOPSIS Deals with forward, backward, and other page flips. This is part of the LearningOnline Network with CAPA project described at http://www.lon-capa.org. =head1 OVERVIEW (empty) =head1 SUBROUTINES =over cleanup() =item addrid() =item fullmove() =item hash_src() =item move() =item get_next_possible_move() =item first_accessible_resource() =item handler() =back =cut