--- rat/lonuserstate.pm 2013/08/16 01:41:05 1.147 +++ rat/lonuserstate.pm 2022/10/05 16:11:25 1.170 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Construct and maintain state and binary representation of course for user # -# $Id: lonuserstate.pm,v 1.147 2013/08/16 01:41:05 raeburn Exp $ +# $Id: lonuserstate.pm,v 1.170 2022/10/05 16:11:25 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -42,7 +42,7 @@ use Safe::Hole; use Opcode; use Apache::lonenc; use Fcntl qw(:flock); -use LONCAPA; +use LONCAPA qw(:DEFAULT :match); use File::Basename; @@ -62,6 +62,9 @@ my %randomorder; # maps to order content my %randomizationcode; # code used to grade folder for bubblesheet exam my %encurl; # URLs in this folder are supposed to be encrypted my %hiddenurl; # this URL (or complete folder) is supposed to be hidden +my %deeplinkout; # this URL (or complete folder) unavailable in deep-link session +my %rescount; # count of unhidden items in each map +my %mapcount; # count of unhidden maps in each map # ----------------------------------- Remove version from URL and store in hash @@ -196,6 +199,10 @@ sub loadmap { $errtext.= '
' .&mt('Map not loaded: The file [_1] does not exist.', "$fn"); + $hash{'map_type_'.$lpc}='none'; + if (&is_advanced($courseid)) { + $errtext .= &error_detail($parent_rid,$courseid,$ispage,$uri); + } return; } @@ -240,6 +247,8 @@ sub loadmap { my @map_ids; my $codechecked; + $rescount{$lpc} = 0; + $mapcount{$lpc} = 0; while (my $token = $parser->get_token) { next if ($token->[0] ne 'S'); @@ -249,6 +258,13 @@ sub loadmap { my $resource_id = &parse_resource($token,$lpc,$ispage,$uri,$courseid); if (defined $resource_id) { push(@map_ids, $resource_id); + if ($hash{'src_'.$lpc.'.'.$resource_id}) { + $rescount{$lpc} ++; + if (($hash{'src_'.$lpc.'.'.$resource_id}=~/\.sequence$/) || + ($hash{'src_'.$lpc.'.'.$resource_id}=~/\.page$/)) { + $mapcount{$lpc} ++; + } + } unless ($codechecked) { my $startsymb = &Apache::lonnet::encode_symb($hash{'map_id_'.$lpc},$resource_id, @@ -278,16 +294,15 @@ sub loadmap { } undef($codechecked); - # Handle randomization and random selection if ($randomize) { - if (!$env{'request.role.adv'}) { + unless (&is_advanced($courseid)) { + # Order of resources is not randomized if user has and advanced role in the course. my $seed; - # In the advanced role, the map's random seed - # parameter is used as the basis for computing the - # seed ... if it has been specified: + # If the map's random seed parameter has been specified + # it is used as the basis for computing the seed ... if (defined($randompickseed{$parent_rid})) { $seed = $randompickseed{$parent_rid}; @@ -327,10 +342,9 @@ sub loadmap { # processing the randomorder parameter if it is set, not # randompick. - @map_ids=&Math::Random::random_permutation(@map_ids); + @map_ids=&Math::Random::random_permutation(@map_ids); } - my $from = shift(@map_ids); my $from_rid = $lpc.'.'.$from; $hash{'map_start_'.$uri} = $from_rid; @@ -355,7 +369,7 @@ sub loadmap { $parser = HTML::TokeParser->new(\$instr); $parser->attr_encoded(1); - # last parse out the mapalias params. Thes provide mnemonic + # last parse out the mapalias params. These provide mnemonic # tags to resources that can be used in conditions while (my $token = $parser->get_token) { @@ -366,6 +380,249 @@ sub loadmap { } } +sub is_advanced { + my ($courseid) = @_; + my $advanced; + if ($env{'request.course.id'}) { + $advanced = (&Apache::lonnet::allowed('adv') eq 'F'); + } else { + $env{'request.course.id'} = $courseid; + $advanced = (&Apache::lonnet::allowed('adv') eq 'F'); + $env{'request.course.id'} = ''; + } + return $advanced; +} + +sub error_detail { + my ($parent_rid,$courseid,$ispage,$uri) = @_; + my $errinfo; + if ($courseid) { + my $courseurl = &Apache::lonnet::courseid_to_courseurl($courseid); + if ($parent_rid =~ /^(\d+)\.(\d+)$/) { + my ($parent_pc,$parent_id) = ($1,$2); + my ($parent_type,$published,$uploaded,$canedit,$role,$switchserver,$audom,$auname, + $editfile,$filerole,$fileswitch,$audomfile,$aunamefile); + if (($parent_pc eq '0') && ($hash{'map_id_1'} =~ m{^/res/($match_domain)/($match_username)/.+\.(sequence|page)$})) { + ($audomfile,$aunamefile) = ($1,$2); + ($editfile,$filerole,$fileswitch) = &canedit_published($audomfile,$aunamefile); + if ($fileswitch) { + unless ((&Apache::lonnet::will_trust('othcoau',$env{'user.domain'},$audomfile)) && + (&Apache::lonnet::will_trust('coaurem',$audomfile,$env{'user.domain'}))) { + undef($editfile); + } + } + $errinfo = &mt('Top level published sequence file is missing.'); + } else { + if ($parent_pc eq '1') { + if ($hash{'map_id_1'} eq "/uploaded$courseurl/default.sequence") { + $uploaded = 1; + if (&Apache::lonnet::allowed('mdc',$courseid)) { + $canedit = 1; + } + $errinfo = &mt('Map is referenced in the top level ([_1]Main Content[_2]) folder.', + '',''); + } elsif ($hash{'map_id_1'} =~ m{^/res/($match_domain)/($match_username)/.+\.(sequence|page)$}) { + ($audom,$auname) = ($1,$2); + ($canedit,$role,$switchserver) = &canedit_published($audom,$auname); + $published = 1; + $errinfo = &mt('Map is referenced in the top level published sequence file.'); + } + } else { + if ($hash{'map_id_'.$parent_pc} =~ m{^\Q/uploaded$courseurl/default_\E\d+\.(sequence|page)$}) { + $uploaded = 1; + if (&Apache::lonnet::allowed('mdc',$courseid)) { + $canedit = 1; + } + } elsif ($hash{'map_id_'.$parent_pc} =~ m{^/res/($match_domain)/($match_username)/.+\.(sequence|page)$}) { + ($audom,$auname) = ($1,$2); + ($canedit,$role,$switchserver) = &canedit_published($audom,$auname); + $published = 1; + } + if (exists($hash{'ids_'.$hash{'map_id_'.$parent_pc}})) { + $parent_type = $hash{'map_type_'.$parent_pc}; + if ($published) { + $errinfo = &mt("Map is referenced in the published $parent_type file: [_1].", + ''.$hash{'map_id_'.$parent_pc}.''); + } else { + my $title = $hash{'title_'.$hash{'ids_'.$hash{'map_id_'.$parent_pc}}}; + if ($title ne '') { + my $mapdesc; + if ($parent_type eq 'sequence') { + $mapdesc = 'folder'; + } else { + $mapdesc = 'composite page'; + } + $errinfo = &mt("Map is referenced in the $mapdesc named: [_1].", + ''.$title.''); + } + my @containers = split(/,/,$hash{'map_hierarchy_'.$parent_pc}); + shift(@containers); + my $folderpath; + foreach my $id (@containers) { + my $name; + if ($id == 1) { + $name = &mt('Main Content'); + } elsif ($hash{'title_'.$hash{'ids_'.$hash{'map_id_'.$id}}} ne '') { + $name = $hash{'title_'.$hash{'ids_'.$hash{'map_id_'.$id}}}; + } + if ($name ne '') { + $folderpath .= $name.' » '; + } + } + if ($title eq '') { + $folderpath =~ s/\Q » \E$//; + } else { + $folderpath .= $title; + } + if ($folderpath) { + $errinfo .= '
'.&mt('Hierarchy is: [_1]', + ''.$folderpath.''); + } + } + } + } + if ($uri =~ m{^/res/($match_domain)/($match_username)/.+\.(sequence|page)$}) { + ($audomfile,$aunamefile) = ($1,$2); + ($editfile,$filerole,$fileswitch) = &canedit_published($audomfile,$aunamefile); + if ($fileswitch) { + unless ((&Apache::lonnet::will_trust('othcoau',$env{'user.domain'},$audomfile)) && + (&Apache::lonnet::will_trust('coaurem',$audomfile,$env{'user.domain'}))) { + undef($editfile); + } + } + } + } + if ($errinfo) { + $errinfo = '
'.$errinfo.'
'; + } + if ($editfile) { + if ($errinfo ne '') { + $errinfo .= '
'; + } + if ($canedit) { + $errinfo .= &mt('One way to rectify this problem is to create and publish the missing file'); + } else { + $errinfo .= &mt('To rectify this problem, create and publish the missing file'); + } + my $fileurl = $uri; + $fileurl =~s{^/res/}{/priv/}; + if ($fileswitch) { + my $rolename = &Apache::lonnet::plaintext($filerole); + my $rolecode; + if ($filerole eq 'au') { + $rolecode = 'au./'.$audomfile.'/'; + } else { + $rolecode = $filerole.'./'.$audomfile.'/'.$aunamefile; + } + $errinfo .= '.
'.&mt('You will need to [_1]switch server[_2].', + '',''); + } else { + &js_escape(\$fileurl); + $errinfo .= ': '.&mt('Create the missing file').''; + } + } + if ($canedit) { + if ($errinfo ne '') { + $errinfo .= '
'; + } + if ($published) { + my $rolename = &Apache::lonnet::plaintext($role); + my $rolecode; + if ($role eq 'au') { + $rolecode = 'au./'.$audom.'/'; + } else { + $rolecode = $role.'./'.$audom.'/'.$auname; + } + if ($editfile) { + $errinfo .= &mt('Another way is to edit the parent map to remove the reference to the missing file'); + } else { + $errinfo .= &mt('To rectify this problem edit the parent map to remove the reference to the missing file'); + } + my $mapurl = $hash{'map_id_'.$parent_pc}; + $mapurl =~s{^/res/}{/priv/}; + if ($switchserver) { + $errinfo .= '.
'; + if ((&Apache::lonnet::will_trust('othcoau',$env{'user.domain'},$audom)) && + (&Apache::lonnet::will_trust('coaurem',$audom,$env{'user.domain'}))) { + $errinfo .= &mt('You will need to [_1]switch server[_2].', + '',''); + } else { + $errinfo .= &mt('Session switch required but prohibited.'); + } + } else { + &js_escape(\$mapurl); + $errinfo .= ': '.&mt('Edit the map').''; + } + } elsif ($uploaded && $courseid) { + my ($dest,$linktext); + my $crstype = &Apache::loncommon::course_type($courseid); + if ($parent_pc eq '1') { + $dest = '/adm/coursedocs?folderpath='.&escape('default&Main%20Content:::::'); + $linktext = &mt('Edit Folder'); + } elsif ($hash{'ids_'.$hash{'map_id_'.$parent_pc}} =~ /^(\d+)\.(\d+)$/) { + my ($editmap,$editidx) = ($1,$2); + my $symb = &Apache::lonnet::encode_symb($hash{'map_id_'.$editmap}, + $editidx,$hash{'map_id_'.$parent_pc}); + $dest = '/adm/coursedocs?command=directnav&symb='.&escape($symb); + if ($parent_type eq 'sequence') { + $linktext = &mt('Edit Folder'); + } else { + $linktext = &mt('Edit Composite Page'); + } + } else { + $dest = '/adm/coursedocs?folderpath='.&escape('default&Main%20Content:::::'); + $linktext = &mt("Edit $crstype"); + } + if ($editfile) { + $errinfo .= &mt("Another way is to use the $crstype Editor to delete the reference to the missing file"); + } else { + $errinfo .= &mt("To rectify this problem use the $crstype Editor to delete the reference to the missing file"); + } + $errinfo .= ': '.$linktext.''; + } + $errinfo .= '
'; + } + } + } + return $errinfo; +} + +sub canedit_published { + my ($audom,$auname) = @_; + my ($canedit,$role,$switchserver); + my $now = time; + if (($auname eq $env{'user.name'}) && ($audom eq $env{'user.domain'})) { + if (exists($env{"user.role.au./$audom/"})) { + my ($start,$end) = split(/\./,$env{"user.role.au./$audom/"}); + unless (($end && $end < $now) || ($start && $start > $now)) { + $canedit = 1; + $role = 'au'; + } + } + } + unless ($canedit) { + foreach my $possrole ('ca','aa') { + if (exists($env{"user.role.$possrole./$audom/$auname"})) { + my ($end,$start) = split(/\./,$env{"user.role.$possrole./$audom/$auname"}); + unless (($end && $end < time) || ($start && $start > time)) { + $canedit = 1; + $role = $possrole; + last; + } + } + } + } + if ($canedit) { + my $auhome = &Apache::lonnet::homeserver($auname,$audom); + my @ids=&Apache::lonnet::current_machine_ids(); + if (($auhome ne 'no_host') && (!grep(/^\Q$auhome\E$/,@ids))) { + $switchserver = $auhome; + } + } + return ($canedit,$role,$switchserver); +} # -------------------------------------------------------------------- Resource # @@ -452,7 +709,11 @@ sub parse_resource { # is not a page. If the resource is a page then it must be # assembled (at fetch time?). - unless ($ispage) { + if ($ispage) { + if ($token->[2]->{'external'} eq 'true') { # external + $turi=~s{^http\://}{/ext/}; + } + } else { $turi=~/\.(\w+)$/; my $embstyle=&Apache::loncommon::fileembstyle($1); if ($token->[2]->{'external'} eq 'true') { # external @@ -467,6 +728,8 @@ sub parse_resource { } elsif ($turi!~/\.(sequence|page)$/) { $turi='/adm/coursedocs/showdoc'.$turi; } + } elsif ($turi=~ m{^/adm/$match_domain/$match_courseid/\d+/ext\.tool$}) { + $turi='/adm/wrapper'.$turi; } elsif ($turi=~/\S/) { # normal non-empty internal resource my $mapdir=$uri; $mapdir=~s/[^\/]+$//; @@ -542,7 +805,9 @@ sub parse_resource { if (($turi=~/\.sequence$/) || ($turi=~/\.page$/)) { $hash{'is_map_'.$rid}=1; - &loadmap($turi,$rid,$courseid); + if ((!$hiddenurl{$rid}) || (&is_advanced($courseid))) { + &loadmap($turi,$rid,$courseid); + } } return $token->[2]->{'id'}; } @@ -698,7 +963,7 @@ sub parse_condition { # Typical attributes: # to=n - Number of the resource the parameter applies to. # type=xx - Type of parameter value (e.g. string_yesno or int_pos). -# name=xxx - Name ofr parameter (e.g. parameter_randompick or parameter_randomorder). +# name=xxx - Name of parameter (e.g. parameter_randompick or parameter_randomorder). # value=xxx - value of the parameter. sub parse_param { @@ -875,7 +1140,7 @@ sub simplify { # new value indicating how far the map has been traversed (the sofar). # sub traceroute { - my ($sofar,$rid,$beenhere,$encflag,$hdnflag)=@_; + my ($sofar,$rid,$beenhere,$encflag,$hdnflag,$cid)=@_; my $newsofar=$sofar=simplify($sofar); unless ($beenhere=~/\&\Q$rid\E\&/) { @@ -899,6 +1164,30 @@ sub traceroute { $retfrid=$rid; } + my (@deeplink,@recurseup); + if ($hash{'is_map_'.$rid}) { + my ($cdom,$cnum) = split(/_/,$cid); + my $mapsrc = $hash{'src_'.$rid}; + my $map_pc = $hash{'map_pc_'.$mapsrc}; + my @pcs = split(/,/,$hash{'map_hierarchy_'.$map_pc}); + shift(@pcs); + @recurseup = map { &Apache::lonnet::declutter($hash{'map_id_'.$_}) } reverse(@pcs); + my $mapname = &Apache::lonnet::declutter(&Apache::lonnet::deversion($mapsrc)); + my $deeplinkval = &get_mapparam($env{'user.name'},$env{'user.domain'},$cnum,$cdom, + $rid,$mapname,'0.deeplink',\@recurseup); + if ($deeplinkval ne '') { + @deeplink = ($deeplinkval,'map'); + } + } else { + my @pcs = split(/,/,$hash{'map_hierarchy_'.$mapid}); + shift(@pcs); + @recurseup = map { &Apache::lonnet::declutter($hash{'map_id_'.$_}) } reverse(@pcs); + @deeplink = &Apache::lonnet::EXT('resource.0.deeplink',$symb,'','','','',$cid,\@recurseup); + } + unless (@deeplink < 2) { + $hash{'deeplinkonly_'.$rid}=join(':',map { &escape($_); } @deeplink); + } + if (defined($hash{'conditions_'.$rid})) { $hash{'conditions_'.$rid}=simplify( '('.$hash{'conditions_'.$rid}.')|('.$sofar.')'); @@ -920,7 +1209,8 @@ sub traceroute { $hash{'map_start_'.$hash{'src_'.$rid}}, $beenhere, $encflag || $encurl{$rid}, - $hdnflag || $hiddenurl{$rid}); + $hdnflag || $hiddenurl{$rid}, + $cid); } } @@ -947,7 +1237,7 @@ sub traceroute { } # Recurse to resoruces that have to's to us. $newsofar=&traceroute($further,$hash{'goesto_'.$id},$beenhere, - $encflag,$hdnflag); + $encflag,$hdnflag,$cid); } } } @@ -1064,7 +1354,7 @@ sub accinit { sub hiddenurls { my $randomoutentry=''; - foreach my $rid (keys %randompick) { + foreach my $rid (keys(%randompick)) { my $rndpick=$randompick{$rid}; my $mpc=$hash{'map_pc_'.$hash{'src_'.$rid}}; # ------------------------------------------- put existing resources into array @@ -1106,6 +1396,14 @@ sub hiddenurls { if ($currentrids[$k]) { $hash{'randomout_'.$currentrids[$k]}=1; my ($mapid,$resid)=split(/\./,$currentrids[$k]); + if ($rescount{$mapid}) { + $rescount{$mapid} --; + } + if ($hash{'is_map_'.$currentrids[$k]}) { + if ($mapcount{$mapid}) { + $mapcount{$mapid} --; + } + } $randomoutentry.='&'. &Apache::lonnet::encode_symb($hash{'map_id_'.$mapid}, $resid, @@ -1115,9 +1413,17 @@ sub hiddenurls { } } # ------------------------------ take care of explicitly hidden urls or folders - foreach my $rid (keys %hiddenurl) { + foreach my $rid (keys(%hiddenurl)) { $hash{'randomout_'.$rid}=1; my ($mapid,$resid)=split(/\./,$rid); + if ($rescount{$mapid}) { + $rescount{$mapid} --; + } + if ($hash{'is_map_'.$rid}) { + if ($mapcount{$mapid}) { + $mapcount{$mapid} --; + } + } $randomoutentry.='&'. &Apache::lonnet::encode_symb($hash{'map_id_'.$mapid},$resid, $hash{'src_'.$rid}).'&'; @@ -1128,10 +1434,53 @@ sub hiddenurls { } } +sub deeplinkouts { + my $deeplinkoutentry; + foreach my $rid (keys(%deeplinkout)) { + $hash{'deeplinkout_'.$rid}=1; + my ($mapid,$resid)=split(/\./,$rid); + $deeplinkoutentry.='&'. + &Apache::lonnet::encode_symb($hash{'map_id_'.$mapid},$resid, + $hash{'src_'.$rid}).'&'; + } +# --------------------------------------- append deeplinkout entry to environment + if ($deeplinkoutentry) { + &Apache::lonnet::appenv({'acc.deeplinkout' => $deeplinkoutentry}); + } +} + +# -------------------------------------- populate big hash with map breadcrumbs + +# Create map_breadcrumbs_$pc from map_hierarchy_$pc by omitting intermediate +# maps not shown in Course Contents table. + +sub mapcrumbs { + my ($cid) = @_; + foreach my $key (keys(%rescount)) { + if ($hash{'map_hierarchy_'.$key}) { + my $skipnext = 0; + foreach my $id (split(/,/,$hash{'map_hierarchy_'.$key}),$key) { + my $rid = $hash{'ids_'.$hash{'map_id_'.$id}}; + unless (($skipnext) || (!&is_advanced($cid) && $hash{'deeplinkout_'.$rid})) { + $hash{'map_breadcrumbs_'.$key} .= "$id,"; + } + unless (($id == 0) || ($id == 1)) { + if ((!$rescount{$id}) || ($rescount{$id} == 1 && $mapcount{$id} == 1)) { + $skipnext = 1; + } else { + $skipnext = 0; + } + } + } + $hash{'map_breadcrumbs_'.$key} =~ s/,$//; + } + } +} + # ---------------------------------------------------- Read map and all submaps sub readmap { - my $short=shift; + my ($short,$critmsg_check) = @_; $short=~s/^\///; # TODO: Hidden dependency on current user: @@ -1148,7 +1497,7 @@ sub readmap { } @cond=('true:normal'); - unless (open(LOCKFILE,">$fn.db.lock")) { + unless (open(LOCKFILE,">","$fn.db.lock")) { # # Most likely a permissions problem on the lockfile or its directory. # @@ -1166,8 +1515,14 @@ sub readmap { &unlink_tmpfiles($fn); } undef %randompick; + undef %randompickseed; + undef %randomorder; + undef %randomizationcode; undef %hiddenurl; undef %encurl; + undef %deeplinkout; + undef %rescount; + undef %mapcount; $retfrid=''; $errtext=''; my ($untiedhash,$untiedparmhash,$tiedhash,$tiedparmhash); # More state flags. @@ -1309,8 +1664,14 @@ sub readmap { $lock=1; } undef %randompick; + undef %randompickseed; + undef %randomorder; + undef %randomizationcode; undef %hiddenurl; undef %encurl; + undef %deeplinkout; + undef %rescount; + undef %mapcount; $errtext=''; $retfrid=''; # @@ -1358,12 +1719,16 @@ sub readmap { # Depends on user must parameterize this as well..or separate as this is: # more part of determining what someone sees on entering a course? +# When lonuserstate::readmap() is called from lonroles.pm, i.e., +# after selecting a role in a course, critical_redirect will be called, +# unless the course has a blocking event in effect, which suppresses +# critical message checking (users without evb priv). +# - my @what=&Apache::lonnet::dump('critical',$env{'user.domain'}, - $env{'user.name'}); - if ($what[0]) { - if (($what[0] ne 'con_lost') && ($what[0]!~/^error\:/)) { - $retfurl='/adm/email?critical=display'; + if ($critmsg_check) { + my ($redirect,$url) = &Apache::loncommon::critical_redirect(); + if ($redirect) { + $retfurl = $url; } } return ($retfurl,$errtext); @@ -1413,7 +1778,6 @@ sub build_tmp_hashes { # Load the map.. note that loadmap may implicitly recurse if the map contains # sub-maps. - &loadmap($uri,'0.0',$short); # The code below only executes if there is a starting point for the map> @@ -1426,7 +1790,7 @@ sub build_tmp_hashes { "request.course.uri" => $uri, "request.course.tied" => time}); $env{'request.course.id'}=$short; - &traceroute('0',$hash{'map_start_'.$uri},'&'); + &traceroute('0',$hash{'map_start_'.$uri},'&','','',$short); &accinit($uri,$short,$fn); &hiddenurls(); } @@ -1460,7 +1824,7 @@ sub build_tmp_hashes { # ---------------------------------------------------- Store away initial state { my $cfh; - if (open($cfh,">$fn.state")) { + if (open($cfh,">","$fn.state")) { print $cfh join("\n",@cond); $gotstate = 1; } else { @@ -1468,6 +1832,92 @@ sub build_tmp_hashes { "Could not write statemap $fn for $uri."); } } + + # Was initial access via a deep-link? + my ($cdom,$cnum) = split(/_/,$short); + if (($cdom ne '') && ($env{'request.deeplink.login'} ne '')) { + my $deeplink_symb = &Apache::loncommon::deeplink_login_symb($cnum,$cdom); + if ($deeplink_symb) { + my ($loginrid,$deeplink_login_pc,$login_hierarchy); + my ($map,$resid,$url) = &Apache::lonnet::decode_symb($deeplink_symb); + $loginrid = $hash{'map_pc_'.&Apache::lonnet::clutter($map)}.'.'.$resid; + if ($deeplink_symb =~ /\.(page|sequence)$/) { + $deeplink_login_pc = $hash{'map_pc_'.&Apache::lonnet::clutter($url)}; + } else { + $deeplink_login_pc = $hash{'map_pc_'.&Apache::lonnet::clutter($map)}; + } + my $deeplink; + if ($hash{'deeplinkonly_'.$loginrid} ne '') { + my @deeplinkinfo = map { &unescape($_); } split(/:/,$hash{'deeplinkonly_'.$loginrid}); + unless (@deeplinkinfo < 2) { + $deeplink = $deeplinkinfo[0]; + } + } + if ($deeplink) { + my $disallow; + my ($state,$others,$listed,$scope,$protect) = split(/,/,$deeplink); + if (($protect ne 'none') && ($protect ne '')) { + my ($acctype,$item) = split(/:/,$protect); + if ($acctype =~ /lti(c|d)$/) { + unless ($env{'request.linkprot'} eq $item.$1.':'.$env{'request.deeplink.login'}) { + $disallow = 1; + } + } elsif ($acctype eq 'key') { + unless ($env{'request.linkkey'} eq $item) { + $disallow = 1; + } + } + } + if ($disallow) { + &Apache::lonnet::delenv('request.deeplink.login'); + if ($env{'request.deeplink.target'} ne '') { + &Apache::lonnet::delenv('request.deeplink.target'); + } + } else { + if ($others eq 'hide') { + my @recfolders; + if ($scope eq 'rec') { + foreach my $key (keys(%hash)) { + if ($key=~/^map_hierarchy_(\d+)$/) { + my $mpc = $1; + my @ids = split(/,/,$hash{$key}); + if (grep(/^$deeplink_login_pc$/,@ids)) { + my $idx; + foreach my $mapid (@ids) { + if ($idx) { + push(@recfolders,$mapid); + } elsif ($mapid == $deeplink_login_pc) { + push(@recfolders,$mapid); + $idx = $mapid; + } + } + push(@recfolders,$mpc); + } + } + } + } + foreach my $key (keys(%hash)) { + if ($key=~/^src_(.+)$/) { + my $rid = $1; + next if ($rid eq '0.0'); + next if ($rid eq $loginrid); + if ($scope ne 'res') { + my $mapid = (split(/\./,$rid))[0]; + next if ($mapid eq $deeplink_login_pc); + if ($scope eq 'rec') { + next if (grep(/^$mapid$/,@recfolders)); + } + } + $deeplinkout{$rid} = 1; + } + } + } + } + &deeplinkouts(); + } + } + } + &mapcrumbs(); return $gotstate; } @@ -1497,7 +1947,7 @@ sub evalstate { if (-e $fn) { my @conditions=(); { - open(my $fh,"<$fn"); + open(my $fh,"<",$fn); @conditions=<$fh>; close($fh); } @@ -1529,6 +1979,181 @@ sub evalstate { return $state; } +sub get_mapparam { + my ($uname,$udom,$cnum,$cdom,$rid,$mapname,$what,$recurseupref) = @_; + unless ($mapname) { return; } + +# ------------------------------------------------- Get coursedata (if present) + my $courseopt=&Apache::lonnet::get_courseresdata($cnum,$cdom); + if (!ref($courseopt)) { + undef($courseopt); + } + +# --------------------------------------------------- Get userdata (if present) + my $useropt=&Apache::lonnet::get_userresdata($uname,$udom); + if (!ref($useropt)) { + undef($useropt); + } + + my @recurseup; + if (ref($recurseupref) eq 'ARRAY') { + @recurseup = @{$recurseupref}; + } + + # Get the section if there is one. + + my $cid = $cdom.'_'.$cnum; + my $csec=$env{'request.course.sec'}; + my $cgroup=''; + my @cgrps=split(/:/,$env{'request.course.groups'}); + if (@cgrps > 0) { + @cgrps = sort(@cgrps); + $cgroup = $cgrps[0]; + } + + my $rwhat=$what; + $what=~s/^parameter\_//; + $what=~s/\_/\./; + + # Build the hash keys for the lookup: + + my $mapparm=$mapname.'___(all).'.$what; + my $recurseparm=$mapname.'___(rec).'.$what; + my $usercourseprefix=$cid; + + my $grplevelm = "$usercourseprefix.[$cgroup].$mapparm"; + my $seclevelm = "$usercourseprefix.[$csec].$mapparm"; + my $courselevelm = "$usercourseprefix.$mapparm"; + + my $grpleveli = "$usercourseprefix.[$cgroup].$recurseparm"; + my $secleveli = "$usercourseprefix.[$csec].$recurseparm"; + my $courseleveli = "$usercourseprefix.$recurseparm"; + + # Check per user + + if ($uname and defined($useropt)) { + if (defined($$useropt{$courselevelm})) { + return $$useropt{$courselevelm}; + } + if (defined($$useropt{$courseleveli})) { + return $$useropt{$courseleveli}; + } + foreach my $item (@recurseup) { + my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what; + if (defined($$useropt{$norecursechk})) { + if ($what =~ /\.(encrypturl|hiddenresource)$/) { + return $$useropt{$norecursechk}; + } else { + last; + } + } + my $recursechk=$usercourseprefix.'.'.$item.'___(rec).'.$what; + if (defined($$useropt{$recursechk})) { + return $$useropt{$recursechk}; + } + } + } + + # Check course -- group + + if ($cgroup ne '' and defined ($courseopt)) { + if (defined($$courseopt{$grplevelm})) { + return $$courseopt{$grplevelm}; + } + if (defined($$courseopt{$grpleveli})) { + return $$courseopt{$grpleveli}; + } + foreach my $item (@recurseup) { + my $norecursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(all).'.$what; + if (defined($$courseopt{$norecursechk})) { + if ($what =~ /\.(encrypturl|hiddenresource)$/) { + return $$courseopt{$norecursechk}; + } else { + last; + } + } + my $recursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(rec).'.$what; + if (defined($$courseopt{$recursechk})) { + return $$courseopt{$recursechk}; + } + } + } + + # Check course -- section + + if ($csec ne '' and defined($courseopt)) { + if (defined($$courseopt{$seclevelm})) { + return $$courseopt{$seclevelm}; + } + if (defined($$courseopt{$secleveli})) { + return $$courseopt{$secleveli}; + } + foreach my $item (@recurseup) { + my $norecursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(all).'.$what; + if (defined($$courseopt{$norecursechk})) { + if ($what =~ /\.(encrypturl|hiddenresource)$/) { + return $$courseopt{$norecursechk}; + } else { + last; + } + } + my $recursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(rec).'.$what; + if (defined($$courseopt{$recursechk})) { + return $$courseopt{$recursechk}; + } + } + } + + # Check the map parameters themselves: + + if ($hash{'param_'.$rid}) { + my @items = split(/\&/,$hash{'param_'.$rid}); + my $thisparm; + foreach my $item (@items) { + my ($esctype,$escname,$escvalue) = ($item =~ /^([^:]+):([^=]+)=(.*)$/); + my $name = &unescape($escname); + my $value = &unescape($escvalue); + if ($name eq $what) { + $thisparm = $value; + last; + } + } + if (defined($thisparm)) { + return $thisparm; + } + } + + # Additional course parameters: + + if (defined($courseopt)) { + if (defined($$courseopt{$courselevelm})) { + return $$courseopt{$courselevelm}; + } + + if (defined($$courseopt{$courseleveli})) { + return $$courseopt{$courseleveli}; + } + + if (@recurseup) { + foreach my $item (@recurseup) { + my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what; + if (defined($$courseopt{$norecursechk})) { + if ($what =~ /\.(encrypturl|hiddenresource)$/) { + return $$courseopt{$norecursechk}; + } else { + last; + } + } + my $recursechk=$usercourseprefix.'.'.$item.'___(rec).'.$what; + if (defined($$courseopt{$recursechk})) { + return $$courseopt{$recursechk}; + } + } + } + } + return undef; +} + # This block seems to have code to manage/detect doubly defined # aliases in maps. 500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.