--- loncom/lonnet/perl/lonnet.pm 2019/01/27 14:40:02 1.1401 +++ loncom/lonnet/perl/lonnet.pm 2019/02/15 20:56:18 1.1405 @@ -1,7 +1,7 @@ # The LearningOnline Network # TCP networking package # -# $Id: lonnet.pm,v 1.1401 2019/01/27 14:40:02 raeburn Exp $ +# $Id: lonnet.pm,v 1.1405 2019/02/15 20:56:18 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -77,7 +77,7 @@ use CGI::Cookie; use Encode; -use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir +use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir $deftex $_64bit %env %protocol %loncaparevs %serverhomeIDs %needsrelease %managerstab); @@ -4359,9 +4359,15 @@ sub embedded_dependency { sub bubblesheet_converter { my ($cdom,$fullpath,$config,$format) = @_; if ((&domain($cdom) ne '') && - ($fullpath =~ m{^\Q$perlvar{'lonDocRoot'}/userfiles/$cdom/$match_courseid/scantron_orig}) && + ($fullpath =~ m{^\Q$perlvar{'lonDocRoot'}/userfiles/$cdom/\E$match_courseid/scantron_orig}) && (-e $fullpath) && (ref($config) eq 'HASH') && ($format ne '')) { - my %csvcols = %{$config}; + my (%csvcols,%csvoptions); + if (ref($config->{'fields'}) eq 'HASH') { + %csvcols = %{$config->{'fields'}}; + } + if (ref($config->{'options'}) eq 'HASH') { + %csvoptions = %{$config->{'options'}}; + } my %csvbynum = reverse(%csvcols); my %scantronconf = &get_scantron_config($format,$cdom); if (keys(%scantronconf)) { @@ -4375,7 +4381,7 @@ sub bubblesheet_converter { ); my @ordered; foreach my $item (sort { $a <=> $b } keys(%bynum)) { - push (@ordered,$bynum{$item})); + push(@ordered,$bynum{$item}); } my %mapstart = ( CODEstart => 'CODE', @@ -4394,23 +4400,75 @@ sub bubblesheet_converter { ); if (open(my $fh,'<',$fullpath)) { my $output; + my %lettdig = &letter_to_digits(); + my %diglett = reverse(%lettdig); + my $numletts = scalar(keys(%lettdig)); + my $num = 0; while (my $line=<$fh>) { + $num ++; + next if (($num == 1) && ($csvoptions{'hdr'} == 1)); $line =~ s{[\r\n]+$}{}; my %found; my @values = split(/,/,$line); my ($qstart,$record); for (my $i=0; $i<@values; $i++) { - if (($qstart ne '') && ($i > $qstart)) { - $found{'FirstQuestion'} .= $values[$i]; - } elsif (exists($csvbynum{$i})) { + if ((($qstart ne '') && ($i > $qstart)) || + ($csvbynum{$i} eq 'FirstQuestion')) { + if ($values[$i] eq '') { + $values[$i] = $scantronconf{'Qoff'}; + } elsif ($scantronconf{'Qon'} eq 'number') { + if ($values[$i] =~ /^[A-Ja-j]$/) { + $values[$i] = $lettdig{uc($values[$i])}; + } + } elsif ($scantronconf{'Qon'} eq 'letter') { + if ($values[$i] =~ /^[0-9]$/) { + $values[$i] = $diglett{$values[$i]}; + } + } else { + if ($values[$i] =~ /^[0-9A-Ja-j]$/) { + my $digit; + if ($values[$i] =~ /^[A-Ja-j]$/) { + $digit = $lettdig{uc($values[$i])}-1; + if ($values[$i] eq 'J') { + $digit += $numletts; + } + } elsif ($values[$i] =~ /^[0-9]$/) { + $digit = $values[$i]-1; + if ($values[$i] eq '0') { + $digit += $numletts; + } + } + my $qval=''; + for (my $j=0; $j<$scantronconf{'Qlength'}; $j++) { + if ($j == $digit) { + $qval .= $scantronconf{'Qon'}; + } else { + $qval .= $scantronconf{'Qoff'}; + } + } + $values[$i] = $qval; + } + } + if (length($values[$i]) > $scantronconf{'Qlength'}) { + $values[$i] = substr($values[$i],0,$scantronconf{'Qlength'}); + } + my $numblank = $scantronconf{'Qlength'} - length($values[$i]); + if ($numblank > 0) { + $values[$i] .= ($scantronconf{'Qoff'} x $numblank); + } if ($csvbynum{$i} eq 'FirstQuestion') { $qstart = $i; + $found{$csvbynum{$i}} = $values[$i]; } else { + $found{'FirstQuestion'} .= $values[$i]; + } + } elsif (exists($csvbynum{$i})) { + if ($csvoptions{'rem'}) { $values[$i] =~ s/^\s+//; - if ($csvbynum{$i} eq 'PaperID') { - while (length($values[$i]) < $scantronconf{$maplength{$csvbynum{$i}}}) { - $values[$i] = '0'.$values[$i]; - } + } + if (($csvbynum{$i} eq 'PaperID') && ($csvoptions{'pad'})) { + while (length($values[$i]) < $scantronconf{$maplength{$csvbynum{$i}}}) { + $values[$i] = '0'.$values[$i]; } } $found{$csvbynum{$i}} = $values[$i]; @@ -4446,6 +4504,22 @@ sub bubblesheet_converter { } } +sub letter_to_digits { + my %lettdig = ( + A => 1, + B => 2, + C => 3, + D => 4, + E => 5, + F => 6, + G => 7, + H => 8, + I => 9, + J => 0, + ); + return %lettdig; +} + sub get_scantron_config { my ($which,$cdom) = @_; my @lines = &get_scantronformat_file($cdom); @@ -4511,7 +4585,7 @@ sub get_scantronformat_file { if (open(my $fh,'<',$perlvar{'lonTabDir'}.'/scantronformat.tab')) { @lines = <$fh>; close($fh); - } + } } else { if (open(my $fh,'<',$perlvar{'lonTabDir'}.'/default_scantronformat.tab')) { @lines = <$fh>; @@ -6709,7 +6783,8 @@ sub set_adhoc_privileges { my ($author,$adv,$rar)= &set_userprivs(\%userroles,\%rolehash); &appenv(\%userroles,[$role,'cm']); &log($env{'user.domain'},$env{'user.name'},$env{'user.home'},"Role ".$spec); - unless ($caller eq 'constructaccess' && $env{'request.course.id'}) { + unless (($caller eq 'constructaccess' && $env{'request.course.id'}) || + ($caller eq 'tiny')) { &appenv( {'request.role' => $spec, 'request.role.domain' => $dcdom, 'request.course.sec' => $sec, @@ -7983,7 +8058,10 @@ sub allowed { if ($env{'user.priv.'.$env{'request.role'}.'./'} =~/\Q$priv\E\&([^\:]*)/) { my $value = $1; - if ($noblockcheck) { + my $deeplinkblock = &deeplink_check($priv,$symb,$uri); + if ($deeplinkblock) { + $thisallowed='D'; + } elsif ($noblockcheck) { $thisallowed.=$value; } else { my @blockers = &has_comm_blocking($priv,$symb,$uri); @@ -8003,7 +8081,10 @@ sub allowed { $refuri=&declutter($refuri); my ($match) = &is_on_map($refuri); if ($match) { - if ($noblockcheck) { + my $deeplinkblock = &deeplink_check($priv,$symb,$refuri); + if ($deeplinkblock) { + $thisallowed='D'; + } elsif ($noblockcheck) { $thisallowed='F'; } else { my @blockers = &has_comm_blocking($priv,$symb,$refuri); @@ -8108,7 +8189,10 @@ sub allowed { =~/\Q$priv\E\&([^\:]*)/) { my $value = $1; if ($priv eq 'bre') { - if ($noblockcheck) { + my $deeplinkblock = &deeplink_check($priv,$symb,$refuri); + if ($deeplinkblock) { + $thisallowed = 'D'; + } elsif ($noblockcheck) { $thisallowed.=$value; } else { my @blockers = &has_comm_blocking($priv,$symb,$refuri); @@ -8276,6 +8360,8 @@ sub allowed { return 'A'; } elsif ($thisallowed eq 'B') { return 'B'; + } elsif ($thisallowed eq 'D') { + return 'D'; } return 'F'; } @@ -8569,6 +8655,93 @@ sub has_comm_blocking { } } +sub deeplink_check { + my ($priv,$symb,$uri) = @_; + return unless ($env{'request.course.id'}); + return unless ($priv eq 'bre'); + return if ($env{'request.state'} eq 'construct'); + return if ($env{'request.role.adv'}); + my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + my (%possibles,@symbs); + if (!$symb) { + $symb = &symbread($uri,1,1,1,\%possibles); + } + if ($symb) { + @symbs = ($symb); + } elsif (keys(%possibles)) { + @symbs = keys(%possibles); + } + + my ($login,$switchrole,$allow); + if ($env{'request.deeplink.login'} =~ m{^\Q/tiny/$cdom/\E(\w+)$}) { + my $key = $1; + my $tinyurl; + my ($result,$cached)=&Apache::lonnet::is_cached_new('tiny',$cdom."\0".$key); + if (defined($cached)) { + $tinyurl = $result; + } else { + my $configuname = &Apache::lonnet::get_domainconfiguser($cdom); + my %currtiny = &Apache::lonnet::get('tiny',[$key],$cdom,$configuname); + if ($currtiny{$key} ne '') { + $tinyurl = $currtiny{$key}; + &Apache::lonnet::do_cache_new('tiny',$cdom."\0".$key,$currtiny{$key},600); + } + } + if ($tinyurl ne '') { + my ($cnumreq,$posslogin) = split(/\&/,$tinyurl); + if ($cnumreq eq $cnum) { + $login = $posslogin; + } else { + $switchrole = 1; + } + } + } + foreach my $symb (@symbs) { + last if ($allow); + my $deeplink = &EXT("resource.0.deeplink",$symb); + if ($deeplink eq '') { + $allow = 1; + } else { + my ($listed,$scope,$access) = split(/,/,$deeplink); + if ($access eq 'any') { + $allow = 1; + } elsif ($login) { + if ($access eq 'only') { + if ($scope eq 'res') { + if ($symb eq $login) { + $allow = 1; + } + } elsif ($scope eq 'map') { +#FIXME Compare map for $env{'request.deeplink.login'} with map for $symb + } elsif ($scope eq 'rec') { +#FIXME Recurse up for $env{'request.deeplink.login'} with map for $symb + } + } else { + my ($acctype,$item) = split(/:/,$access); + if (($acctype eq 'lti') && ($env{'user.linkprotector'})) { + if (grep(/^\Q$item\E$/,split(/,/,$env{'user.linkprotector'}))) { + my %tinyurls = &get('tiny',[$symb],$cdom,$cnum); + if (grep(/\Q$tinyurls{$symb}\E$/,split(/,/,$env{'user.linkproturis'}))) { + $allow = 1; + } + } + } elsif (($acctype eq 'key') && ($env{'user.deeplinkkey'})) { + if (grep(/^\Q$item\E$/,split(/,/,$env{'user.deeplinkkey'}))) { + my %tinyurls = &get('tiny',[$symb],$cdom,$cnum); + if (grep(/\Q$tinyurls{$symb}\E$/,split(/,/,$env{'user.keyedlinkuri'}))) { + $allow = 1; + } + } + } + } + } + } + } + return if ($allow); + return 1; +} + # -------------------------------- Deversion and split uri into path an filename # @@ -14638,6 +14811,11 @@ BEGIN { } +# ------------- set default texengine (domain default overrides this) +{ + $deftex = LONCAPA::texengine(); +} + $memcache=new Cache::Memcached({'servers' => ['127.0.0.1:11211'], 'compress_threshold'=> 20_000, }); @@ -14977,6 +15155,7 @@ prevents recursive calls to &allowed. 2: browse allowed A: passphrase authentication needed B: access temporarily blocked because of a blocking event in a course. + D: access blocked because access is required via session initiated via deep-link =item * @@ -15362,7 +15541,6 @@ Returns: for the sheet of paper FirstName - column that the first name starts in FirstNameLength - number of columns that the first name spans - LastName - column that the last name starts in LastNameLength - number of columns that the last name spans BubblesPerRow - number of bubbles available in each row used to