--- rat/lonpageflip.pm 2017/11/16 13:42:01 1.97 +++ rat/lonpageflip.pm 2021/08/21 03:42:02 1.110 @@ -2,7 +2,7 @@ # # Page flip handler # -# $Id: lonpageflip.pm,v 1.97 2017/11/16 13:42:01 raeburn Exp $ +# $Id: lonpageflip.pm,v 1.110 2021/08/21 03:42:02 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -99,9 +99,14 @@ sub hash_src { } sub move { - my ($next,$endupmap,$direction) = @_; + 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); @@ -112,17 +117,93 @@ sub move { if ($url eq '' || $symb eq '') { $allowed = 0; } else { - my $priv = &Apache::lonnet::allowed('bre',$url,$symb); - $allowed = (($priv eq 'F') || ($priv eq '2')); + 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) = map { &unescape($_); } split(/:/,$hash{'deeplinkonly_'.$next}); + my ($state,$others,$listed,$scope,$protect) = split(/,/,$value); + unless (($state eq 'both') || ($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) = map { &unescape($_); } split(/:/,$hash{'deeplinkonly_'.$prev}); + my ($state,$others,$listed,$scope,$protect) = split(/,/,$value); + unless (($state eq 'both') || ($hash{'is_map_'.$prev})) { + 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_'.$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} + (!$env{'request.role.adv'}) + && (($hash{'randomout_'.$next}) + || ($deeplinkonly) + || ($hash{'deeplinkout_'.$next})) ) || (!$allowed) ) @@ -157,7 +238,7 @@ sub get_next_possible_move { } if ($thiscond>$mincond) { $mincond=$thiscond; } } - } + } foreach my $id (split(/\,/,$posnext)) { my ($linkid,$condval)=split(/\:/,$id); if ($condval>=$mincond) { @@ -199,7 +280,7 @@ sub get_next_possible_move { } if ($thiscond>$mincond) { $mincond=$thiscond; } } - } + } foreach my $id (split(/\,/,$posnext)) { my ($linkid,$condval)=split(/\:/,$id); if ($condval>=$mincond) { @@ -232,22 +313,32 @@ sub first_accessible_resource { 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 (%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); } - if (!&Apache::lonnet::allowed('bre',$url,$args{'symb'})) { + 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'); + my ($newrid,$newmap)=&move($hash{'first_rid'},$hash{'first_mapurl'},'forward',1); # 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); - } + 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; @@ -280,7 +371,7 @@ sub first_answerable_ressymb { } sub check_http_req { - my ($srcref) = @_; + my ($srcref,$hostname) = @_; return unless (ref($srcref) eq 'SCALAR'); my $usehttp; if ($env{'request.course.id'}) { @@ -289,11 +380,19 @@ sub check_http_req { if (($$srcref =~ m{^\Q/public/$cdom/$cnum/syllabus\E($|\?)}) && ($ENV{'SERVER_PORT'} == 443) && ($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'} =~ m{^http://})) { - $$srcref .= (($$srcref =~/\?/)? '&':'?') . 'usehttp=1'; - $usehttp = 1; + 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)) { - $usehttp = 1; + unless ((&Apache::lonnet::uses_sts()) || + (&Apache::lonnet::waf_allssl($hostname))) { + my ($url,$anchor) = ($$srcref =~ /^([^\#]+)(?:|(\#[^\#]+))$/); + $$srcref = $url . (($$srcref =~/\?/)? '&':'?') . 'usehttp=1' .$anchor; + $usehttp = 1; + } } } return $usehttp; @@ -321,6 +420,25 @@ setTimeout(function() { 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'}; + 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 { @@ -338,7 +456,7 @@ sub handler { my $multichoice=0; my %multichoicehash=(); my %prog_state=(); - my ($redirecturl,$redirectsymb,$enc,$anchor); + my ($redirecturl,$redirectsymb,$enc,$anchor,$deeplinklevel); my $next=''; my $hostname = $r->hostname(); my @possibilities=(); @@ -360,7 +478,7 @@ sub handler { } } elsif ($direction eq 'firstanswerable') { my $furl = &first_answerable_ressymb(); - my $usehttp = &check_http_req(\$furl); + my $usehttp = &check_http_req(\$furl,$hostname); if (($usehttp) && ($hostname ne '')) { $furl='http://'.$hostname.$furl; } else { @@ -401,12 +519,12 @@ sub handler { my $preamble = '
'. '
'. &mt('Your course session is being updated because of recent changes by course personnel.'). - ' '.&mt('Please be patient.').'
'. + ' '.&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')); + &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,&mt('Finished!')); if ($ferr) { &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); my $requrl = $r->uri; @@ -425,7 +543,7 @@ sub handler { } if ($direction eq 'firstres') { my $furl=&first_accessible_resource(); - my $usehttp = &check_http_req(\$furl); + my $usehttp = &check_http_req(\$furl,$hostname); if (($usehttp) && ($hostname ne '')) { $furl='http://'.$hostname.$furl; } else { @@ -442,7 +560,7 @@ sub handler { return REDIRECT; } } - if ($direction eq 'return') { + if ($direction eq 'return') { # -------------------------------------------------------- Return to last known my ($newloc,$usehttp); if (($last) && (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'.db', @@ -451,11 +569,15 @@ sub handler { $id=$hash{'map_pc_'.&Apache::lonnet::clutter($murl)}.'.'.$id; $newloc=$hash{'src_'.$id}; if ($newloc) { - $usehttp = &check_http_req(\$newloc); + $usehttp = &check_http_req(\$newloc,$hostname); if ($hash{'encrypted_'.$id}) { $newloc=&Apache::lonenc::encrypted($newloc); - } elsif ($newloc =~ m{^(/adm/wrapper/ext/[^\#]+)\#([^\#]+)$}) { - $newloc = $1.&escape('#').$2; + } + if ($newloc =~ m{^(/adm/wrapper/ext/[^\#]+)(?:|(\#[^\#]+))$}) { + my ($url,$anchor) = ($1,$2); + if ($anchor) { + $newloc = $url.(($url=~/\?/)?'&':'?').'symb='.&escape($last).$anchor; + } } } else { $newloc='/adm/navmaps'; @@ -491,7 +613,6 @@ sub handler { $last=$hash{'last_known'}; untie(%hash); } - my $newloc; if ($last) { $currenturl=&Apache::lonnet::clutter((&Apache::lonnet::decode_symb($last))[2]); } else { @@ -564,6 +685,15 @@ sub handler { } 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) = map { &unescape($_); } + split(/:/,$position_deeplink); + } + } } # ----------------- The program must come past this point to untie the big hash untie(%hash); @@ -587,7 +717,7 @@ sub handler { # ------------------------------------- Check for and display critical messages my ($redirect, $url) = &Apache::loncommon::critical_redirect(300,'flip'); unless ($redirect) { - my $usehttp = &check_http_req(\$redirecturl); + my $usehttp = &check_http_req(\$redirecturl,$hostname); if (($usehttp) && ($hostname ne '')) { $url='http://'.$hostname.$redirecturl; } else { @@ -618,6 +748,11 @@ sub handler { &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', @@ -649,7 +784,7 @@ $lt{'pick'}: ENDSTART foreach my $id (@possibilities) { my $src = $multichoicehash{'src_'.$id}; - my $usehttp = &check_http_req(\$src); + my $usehttp = &check_http_req(\$src,$hostname); if (($usehttp) && ($hostname ne '')) { $src = 'http://'.$hostname.$src; } @@ -685,17 +820,29 @@ ENDSTART } else { $r->print(&Apache::lonplacementtest::showresult(1)); } - } else { + } else { $r->print( - &Apache::loncommon::start_page('No Resource') - .'

'.$lt{'title'}.'

' - .'

'.$lt{'explain'}.'

'); + &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 ((!@possibilities) && ($reinitcheck)) { + if ($deeplinklevel) { + $r->print( + &Apache::lonhtmlcommon::actionbox( + [''.$lt{'back'}.''])); + } elsif ((!@possibilities) && ($reinitcheck)) { $r->print( &Apache::lonhtmlcommon::actionbox( [''.$lt{'nav'}.''