--- rat/lonpageflip.pm 2002/08/18 21:45:41 1.29 +++ rat/lonpageflip.pm 2021/02/18 14:48:02 1.102 @@ -2,7 +2,7 @@ # # Page flip handler # -# $Id: lonpageflip.pm,v 1.29 2002/08/18 21:45:41 www Exp $ +# $Id: lonpageflip.pm,v 1.102 2021/02/18 14:48:02 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -26,23 +26,19 @@ # # http://www.lon-capa.org/ # -# (Page Handler -# -# (TeX Content Handler -# -# 05/29/00,05/30 Gerd Kortemeyer) -# 08/30,08/31,09/06,09/14,09/15,09/16,09/19,09/20,09/21,09/23, -# 10/02 Gerd Kortemeyer) -# -# 10/03,10/05,10/06,10/07,10/09,10/10,10/11,10/16,10/17, -# 11/14,11/16, -# 10/01/01,05/01,05/28,07/05 Gerd Kortemeyer + + package Apache::lonpageflip; use strict; +use LONCAPA; use Apache::Constants qw(:common :http REDIRECT); -use Apache::lonnet(); +use Apache::lonnet; +use Apache::loncommon(); +use Apache::lonnavmaps(); +use Apache::lonuserstate; +use Apache::lonlocal; use HTML::TokeParser; use GDBM_File; @@ -50,6 +46,16 @@ use GDBM_File; 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; } @@ -65,7 +71,7 @@ sub addrid { sub fullmove { my ($rid,$mapurl,$direction)=@_; - if (tie(%hash,'GDBM_File',$ENV{'request.course.fn'}.'.db', + if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'.db', &GDBM_READER(),0640)) { ($rid,$mapurl)=&move($rid,$mapurl,$direction); untie(%hash); @@ -73,7 +79,83 @@ sub fullmove { 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) = @_; + my $safecount=0; + my $allowed=0; + my $deeplinkonly=0; + 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 $priv = &Apache::lonnet::allowed('bre',$url,$symb); + $allowed = (($priv eq 'F') || ($priv eq '2')); + } + $deeplinkonly = 0; + if ($hash{'deeplinkonly_'.$next}) { + my ($value,$level) = split(/:/,$hash{'deeplinkonly_'.$next}); + if ($level eq 'resource') { + $deeplinkonly = 1; + } elsif ($level eq 'map') { + if ($mapid != $prevmapid) { + $deeplinkonly = 1; + } + } + } elsif ($hash{'deeplinkonly_'.$prev}) { + my ($value,$level) = split(/:/,$hash{'deeplinkonly_'.$prev}); + if ($level eq 'resource') { + $deeplinkonly = 1; + } elsif ($level eq 'map') { + if ($mapid != $prevmapid) { + $deeplinkonly = 1; + } + } + } + $safecount++; + } while ( ($next) + && ($next!~/\,/) + && ( + (!$hash{'src_'.$next}) + || ( + (!$env{'request.role.adv'}) + && (($hash{'randomout_'.$next}) + || ($deeplinkonly)) + ) + || (!$allowed) + ) + && ($safecount<10000)); + + return ($next,$endupmap); +} + +sub get_next_possible_move { my ($rid,$mapurl,$direction)=@_; my $startoutrid=$rid; @@ -83,71 +165,83 @@ sub move { my $posnext=''; if ($direction eq 'forward') { # --------------------------------------------------------------------- Forward - if ($hash{'type_'.$rid} eq 'finish') { - $rid=$hash{'ids_'.&Apache::lonnet::clutter($mapurl)}; + while ($hash{'type_'.$rid} eq 'finish') { + $rid=$hash{'ids_'.$hash{'map_id_'.(split(/\./,$rid))[0]}}; } - map { - my $thiscond= - &Apache::lonnet::directcondval($hash{'condid_'.$hash{'undercond_'.$_}}); - if ($thiscond>=$mincond) { + 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.=','.$_.':'.$thiscond; + $posnext.=','.$id.':'.$thiscond; } else { - $posnext=$_.':'.$thiscond; + $posnext=$id.':'.$thiscond; } if ($thiscond>$mincond) { $mincond=$thiscond; } } - } split(/\,/,$hash{'to_'.$rid}); - map { - my ($linkid,$condval)=split(/\:/,$_); + } + foreach my $id (split(/\,/,$posnext)) { + my ($linkid,$condval)=split(/\:/,$id); if ($condval>=$mincond) { $next=&addrid($next,$hash{'goesto_'.$linkid}, $hash{'condid_'.$hash{'undercond_'.$linkid}}); } - } split(/\,/,$posnext); + } 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 - if ($hash{'type_'.$rid} eq 'start') { - $rid=$hash{'ids_'.&Apache::lonnet::clutter($mapurl)}; - } - map { - my $thiscond= - &Apache::lonnet::directcondval($hash{'condid_'.$hash{'undercond_'.$_}}); - if ($thiscond>=$mincond) { - if ($posnext) { - $posnext.=','.$_.':'.$thiscond; - } else { - $posnext=$_.':'.$thiscond; - } - if ($thiscond>$mincond) { $mincond=$thiscond; } - } - } split(/\,/,$hash{'from_'.$rid}); - map { - my ($linkid,$condval)=split(/\:/,$_); - if ($condval>=$mincond) { - $next=&addrid($next,$hash{'comesfrom_'.$linkid}, - $hash{'condid_'.$hash{'undercond_'.$linkid}}); - } - } split(/\,/,$posnext); + 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); @@ -157,6 +251,106 @@ sub move { 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; + my ($url,$args) = split(/\?/,$furl); + foreach my $pair (split(/\&/,$args)) { + my ($name,$value) = split(/=/,$pair); + $args{&unescape($name)} = &unescape($value); + } + if (!&Apache::lonnet::allowed('bre',$url,$args{'symb'})) { +# 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'); +# Build the new URL + 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) = @_; + 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()) { + $$srcref .= (($$srcref =~/\?/)? '&':'?') . 'usehttp=1'; + $usehttp = 1; + } + } elsif (($$srcref =~ m{^\Q/adm/wrapper/ext/\E(?!https:)}) && + ($ENV{'SERVER_PORT'} == 443)) { + unless (&Apache::lonnet::uses_sts()) { + 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 +} + # ================================================================ Main Handler sub handler { @@ -165,71 +359,205 @@ sub handler { # ------------------------------------------- Set document type for header only if ($r->header_only) { - $r->content_type('text/html'); - $r->send_http_header; - return OK; + &Apache::loncommon::content_type($r,'text/html'); + $r->send_http_header; + return OK; } my %cachehash=(); my $multichoice=0; my %multichoicehash=(); - my $redirecturl=''; + my %prog_state=(); + my ($redirecturl,$redirectsymb,$enc,$anchor,$deeplinklevel); my $next=''; + my $hostname = $r->hostname(); my @possibilities=(); - if (($ENV{'form.postdata'})&&($ENV{'request.course.fn'})) { - $ENV{'form.postdata'}=~/(\w+)\:(.*)/; - my $direction=$1; - my $currenturl=$2; + &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') { -# -------------------------------------------------------- Return to last known - 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_known'}; - untie(%hash); + $last=$hash{'last_known'}; + untie(%hash); + } + } elsif ($direction eq 'firstanswerable') { + my $furl = &first_answerable_ressymb(); + my $usehttp = &check_http_req(\$furl); + 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 = '
-Please click on the the resource you intend to access: +$lt{'pick'}:
-
Title | Type |
---|
$lt{'titleheader'} | $lt{'type'} | |
---|---|---|
'. - $multichoicehash{'title_'.$_}. - ' | '.$multichoicehash{'type_'.$_}. + &add_get_param($src, + {'symb' => + $multichoicehash{'symb_'.$id}, + }).'">'. + $multichoicehash{'title_'.$id}. + ' | '.$multichoicehash{'type_'.$id}. ' |