--- loncom/lonnet/perl/lonnet.pm 2018/04/29 00:56:53 1.1172.2.93.4.10 +++ loncom/lonnet/perl/lonnet.pm 2021/05/11 17:51:23 1.1455 @@ -1,7 +1,7 @@ # The LearningOnline Network # TCP networking package # -# $Id: lonnet.pm,v 1.1172.2.93.4.10 2018/04/29 00:56:53 raeburn Exp $ +# $Id: lonnet.pm,v 1.1455 2021/05/11 17:51:23 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -71,13 +71,15 @@ delayed. package Apache::lonnet; use strict; -use LWP::UserAgent(); use HTTP::Date; use Image::Magick; +use CGI::Cookie; -use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir +use Encode; + +use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir $deftex $_64bit %env %protocol %loncaparevs %serverhomeIDs %needsrelease - %managerstab); + %managerstab $passwdmin); my (%badServerCache, $memcache, %courselogs, %accesshash, %domainrolehash, %userrolehash, $processmarker, $dumpcount, %coursedombuf, @@ -94,10 +96,13 @@ use Cache::Memcached; use Digest::MD5; use Math::Random; use File::MMagic; +use Net::CIDR; use LONCAPA qw(:DEFAULT :match); use LONCAPA::Configuration; use LONCAPA::lonmetadata; use LONCAPA::Lond; +use LONCAPA::LWPReq; +use LONCAPA::transliterate; use File::Copy; @@ -109,6 +114,7 @@ require Exporter; our @ISA = qw (Exporter); our @EXPORT = qw(%env); + # ------------------------------------ Logging (parameters, docs, slots, roles) { my $logid; @@ -123,19 +129,20 @@ our @EXPORT = qw(%env); $logid ++; my $now = time(); my $id=$now.'00000'.$$.'00000'.$logid; - my $logentry = { - $id => { - 'exe_uname' => $env{'user.name'}, - 'exe_udom' => $env{'user.domain'}, - 'exe_time' => $now, - 'exe_ip' => $ENV{'REMOTE_ADDR'}, - 'delflag' => $delflag, - 'logentry' => $storehash, - 'uname' => $uname, - 'udom' => $udom, - } + my $ip = &get_requestor_ip(); + my $logentry = { + $id => { + 'exe_uname' => $env{'user.name'}, + 'exe_udom' => $env{'user.domain'}, + 'exe_time' => $now, + 'exe_ip' => $ip, + 'delflag' => $delflag, + 'logentry' => $storehash, + 'uname' => $uname, + 'udom' => $udom, + } }; - return &put('nohist_'.$hash_name,$logentry,$cdom,$cnum); + return &put('nohist_'.$hash_name,$logentry,$cdom,$cnum); } } @@ -180,7 +187,7 @@ sub create_connection { Type => SOCK_STREAM, Timeout => 10); return 0 if (!$client); - print $client (join(':',$hostname,$lonid,&machine_ids($hostname))."\n"); + print $client (join(':',$hostname,$lonid,&machine_ids($hostname),$loncaparevs{$lonid})."\n"); my $result = <$client>; chomp($result); return 1 if ($result eq 'done'); @@ -225,6 +232,54 @@ sub get_server_distarch { return; } +sub get_servercerts_info { + my ($lonhost,$hostname,$context) = @_; + return if ($lonhost eq ''); + if ($hostname eq '') { + $hostname = &hostname($lonhost); + } + return if ($hostname eq ''); + my ($rep,$uselocal); + if ($context eq 'install') { + $uselocal = 1; + } elsif (grep { $_ eq $lonhost } ¤t_machine_ids()) { + $uselocal = 1; + } + if (($context ne 'cgi') && ($context ne 'install') && ($uselocal)) { + my $distro = (split(/\:/,&get_server_distarch($lonhost)))[0]; + if ($distro eq '') { + $uselocal = 0; + } elsif ($distro =~ /^(?:centos|redhat|scientific)(\d+)$/) { + if ($1 < 6) { + $uselocal = 0; + } + } elsif ($distro =~ /^(?:sles)(\d+)$/) { + if ($1 < 12) { + $uselocal = 0; + } + } + } + if ($uselocal) { + $rep = LONCAPA::Lond::server_certs(\%perlvar,$lonhost,$hostname); + } else { + $rep=&reply('servercerts',$lonhost); + } + my ($result,%returnhash); + if (($rep=~/^(refused|rejected|error)/) || ($rep eq 'con_lost') || + ($rep eq 'unknown_cmd')) { + $result = $rep; + } else { + $result = 'ok'; + my @pairs=split(/\&/,$rep); + foreach my $item (@pairs) { + my ($key,$value)=split(/=/,$item,2); + my $what = &unescape($key); + $returnhash{$what}=&thaw_unescape($value); + } + } + return ($result,\%returnhash); +} + sub get_server_loncaparev { my ($dom,$lonhost,$ignore_cache,$caller) = @_; if (defined($lonhost)) { @@ -259,13 +314,12 @@ sub get_server_loncaparev { $answer = &reply('serverloncaparev',$lonhost); if (($answer eq 'unknown_cmd') || ($answer eq 'con_lost')) { if ($caller eq 'loncron') { - my $ua=new LWP::UserAgent; - $ua->timeout(4); + my $hostname = &hostname($lonhost); my $protocol = $protocol{$lonhost}; $protocol = 'http' if ($protocol ne 'https'); - my $url = $protocol.'://'.&hostname($lonhost).'/adm/about.html'; + my $url = $protocol.'://'.$hostname.'/adm/about.html'; my $request=new HTTP::Request('GET',$url); - my $response=$ua->request($request); + my $response=&LONCAPA::LWPReq::makerequest($lonhost,$request,'',\%perlvar,4,1); unless ($response->is_error()) { my $content = $response->content; if ($content =~ /
VERSION\:\s*([\w.\-]+)<\/p>/) {
@@ -356,7 +410,8 @@ sub remote_devalidate_cache {
my $items;
return unless (ref($cachekeys) eq 'ARRAY');
my $cachestr = join('&',@{$cachekeys});
- return &reply('devalidatecache:'.&escape($cachestr),$lonhost);
+ my $response = &reply('devalidatecache:'.&escape($cachestr),$lonhost);
+ return $response;
}
# -------------------------------------------------- Non-critical communication
@@ -388,7 +443,7 @@ sub subreply {
} else {
&create_connection(&hostname($server),$server);
}
- sleep(0.1); # Try again later if failed connection.
+ sleep(0.1); # Try again later if failed connection.
}
my $answer;
if ($client) {
@@ -407,8 +462,26 @@ sub reply {
unless (defined(&hostname($server))) { return 'no_such_host'; }
my $answer=subreply($cmd,$server);
if (($answer=~/^refused/) || ($answer=~/^rejected/)) {
- &logthis("WARNING:".
- " $cmd to $server returned $answer");
+ my $logged = $cmd;
+ if ($cmd =~ /^encrypt:([^:]+):/) {
+ my $subcmd = $1;
+ if (($subcmd eq 'auth') || ($subcmd eq 'passwd') ||
+ ($subcmd eq 'changeuserauth') || ($subcmd eq 'makeuser') ||
+ ($subcmd eq 'putdom') || ($subcmd eq 'autoexportgrades')) {
+ (undef,undef,my @rest) = split(/:/,$cmd);
+ if (($subcmd eq 'auth') || ($subcmd eq 'putdom')) {
+ splice(@rest,2,1,'Hidden');
+ } elsif ($subcmd eq 'passwd') {
+ splice(@rest,2,2,('Hidden','Hidden'));
+ } elsif (($subcmd eq 'changeuserauth') || ($subcmd eq 'makeuser') ||
+ ($subcmd eq 'autoexportgrades')) {
+ splice(@rest,3,1,'Hidden');
+ }
+ $logged = join(':',('encrypt:'.$subcmd,@rest));
+ }
+ }
+ &logthis("WARNING:".
+ " $logged to $server returned $answer");
}
return $answer;
}
@@ -443,7 +516,7 @@ sub reconlonc {
&logthis("lonc at pid $loncpid responding, sending USR1");
kill USR1 => $loncpid;
sleep 1;
- } else {
+ } else {
&logthis(
"WARNING:".
" lonc at pid $loncpid not responding, giving up");
@@ -603,21 +676,42 @@ sub transfer_profile_to_env {
sub check_for_valid_session {
my ($r,$name,$userhashref,$domref) = @_;
my %cookies=CGI::Cookie->parse($r->header_in('Cookie'));
- if ($name eq '') {
- $name = 'lonID';
- }
- my $lonid=$cookies{$name};
- return undef if (!$lonid);
-
- my $handle=&LONCAPA::clean_handle($lonid->value);
- my $lonidsdir;
+ my ($lonidsdir,$linkname,$pubname,$secure,$lonid);
if ($name eq 'lonDAV') {
$lonidsdir=$r->dir_config('lonDAVsessDir');
} else {
$lonidsdir=$r->dir_config('lonIDsDir');
+ if ($name eq '') {
+ $name = 'lonID';
+ }
+ }
+ if ($name eq 'lonID') {
+ $secure = 'lonSID';
+ $linkname = 'lonLinkID';
+ $pubname = 'lonPubID';
+ if (exists($cookies{$secure})) {
+ $lonid=$cookies{$secure};
+ } elsif (exists($cookies{$name})) {
+ $lonid=$cookies{$name};
+ } elsif ((exists($cookies{$linkname})) && ($ENV{'SERVER_PORT'} != 443)) {
+ $lonid=$cookies{$linkname};
+ } elsif (exists($cookies{$pubname})) {
+ $lonid=$cookies{$pubname};
+ }
+ } else {
+ $lonid=$cookies{$name};
+ }
+ return undef if (!$lonid);
+
+ my $handle=&LONCAPA::clean_handle($lonid->value);
+ if (-l "$lonidsdir/$handle.id") {
+ my $link = readlink("$lonidsdir/$handle.id");
+ if ((-e $link) && ($link =~ m{^\Q$lonidsdir\E/(.+)\.id$})) {
+ $handle = $1;
+ }
}
if (!-e "$lonidsdir/$handle.id") {
- if ((ref($domref)) && ($name eq 'lonID') &&
+ if ((ref($domref)) && ($name eq 'lonID') &&
($handle =~ /^($match_username)\_\d+\_($match_domain)\_(.+)$/)) {
my ($possuname,$possudom,$possuhome) = ($1,$2,$3);
if ((&domain($possudom) ne '') && (&homeserver($possuname,$possudom) eq $possuhome)) {
@@ -639,13 +733,23 @@ sub check_for_valid_session {
if (!defined($disk_env{'user.name'})
|| !defined($disk_env{'user.domain'})) {
+ untie(%disk_env);
return undef;
}
if (ref($userhashref) eq 'HASH') {
$userhashref->{'name'} = $disk_env{'user.name'};
$userhashref->{'domain'} = $disk_env{'user.domain'};
+ if ($disk_env{'request.role'}) {
+ $userhashref->{'role'} = $disk_env{'request.role'};
+ }
+ $userhashref->{'lti'} = $disk_env{'request.lti.login'};
+ if ($userhashref->{'lti'}) {
+ $userhashref->{'ltitarget'} = $disk_env{'request.lti.target'};
+ $userhashref->{'ltiuri'} = $disk_env{'request.lti.uri'};
+ }
}
+ untie(%disk_env);
return $handle;
}
@@ -670,6 +774,37 @@ sub timed_flock {
}
}
+sub get_sessionfile_vars {
+ my ($handle,$lonidsdir,$storearr) = @_;
+ my %returnhash;
+ unless (ref($storearr) eq 'ARRAY') {
+ return %returnhash;
+ }
+ if (-l "$lonidsdir/$handle.id") {
+ my $link = readlink("$lonidsdir/$handle.id");
+ if ((-e $link) && ($link =~ m{^\Q$lonidsdir\E/(.+)\.id$})) {
+ $handle = $1;
+ }
+ }
+ if ((-e "$lonidsdir/$handle.id") &&
+ ($handle =~ /^($match_username)\_\d+\_($match_domain)\_(.+)$/)) {
+ my ($possuname,$possudom,$possuhome) = ($1,$2,$3);
+ if ((&domain($possudom) ne '') && (&homeserver($possuname,$possudom) eq $possuhome)) {
+ if (open(my $idf,'+<',"$lonidsdir/$handle.id")) {
+ flock($idf,LOCK_SH);
+ if (tie(my %disk_env,'GDBM_File',"$lonidsdir/$handle.id",
+ &GDBM_READER(),0640)) {
+ foreach my $item (@{$storearr}) {
+ $returnhash{$item} = $disk_env{$item};
+ }
+ untie(%disk_env);
+ }
+ }
+ }
+ }
+ return %returnhash;
+}
+
# ---------------------------------------------------------- Append Environment
sub appenv {
@@ -695,16 +830,19 @@ sub appenv {
$env{$key}=$newenv->{$key};
}
}
- my $opened = open(my $env_file,'+<',$env{'user.environment'});
- if ($opened
- && &timed_flock($env_file,LOCK_EX)
- &&
- tie(my %disk_env,'GDBM_File',$env{'user.environment'},
- (&GDBM_WRITER()|&GDBM_NOLOCK()),0640)) {
- while (my ($key,$value) = each(%{$newenv})) {
- $disk_env{$key} = $value;
- }
- untie(%disk_env);
+ my $lonids = $perlvar{'lonIDsDir'};
+ if ($env{'user.environment'} =~ m{^\Q$lonids/\E$match_username\_\d+\_$match_domain\_[\w\-.]+\.id$}) {
+ my $opened = open(my $env_file,'+<',$env{'user.environment'});
+ if ($opened
+ && &timed_flock($env_file,LOCK_EX)
+ &&
+ tie(my %disk_env,'GDBM_File',$env{'user.environment'},
+ (&GDBM_WRITER()|&GDBM_NOLOCK()),0640)) {
+ while (my ($key,$value) = each(%{$newenv})) {
+ $disk_env{$key} = $value;
+ }
+ untie(%disk_env);
+ }
}
}
return 'ok';
@@ -820,6 +958,7 @@ sub userload {
while ($filename=readdir(LONIDS)) {
next if ($filename eq '.' || $filename eq '..');
next if ($filename =~ /publicuser_\d+\.id/);
+ next if ($filename =~ /^[a-f0-9]+_linked\.id$/);
my ($mtime)=(stat($perlvar{'lonIDsDir'}.'/'.$filename))[9];
if ($curtime-$mtime < 1800) { $numusers++; }
}
@@ -837,7 +976,7 @@ sub userload {
# ------------------------------ Find server with least workload from spare.tab
sub spareserver {
- my ($loadpercent,$userloadpercent,$want_server_name,$udom) = @_;
+ my ($r,$loadpercent,$userloadpercent,$want_server_name,$udom) = @_;
my $spare_server;
if ($userloadpercent !~ /\d/) { $userloadpercent=0; }
my $lowest_load=($loadpercent > $userloadpercent) ? $loadpercent
@@ -875,13 +1014,15 @@ sub spareserver {
}
if (!$want_server_name) {
- my $protocol = 'http';
- if ($protocol{$spare_server} eq 'https') {
- $protocol = $protocol{$spare_server};
- }
if (defined($spare_server)) {
my $hostname = &hostname($spare_server);
if (defined($hostname)) {
+ my $protocol = 'http';
+ if ($protocol{$spare_server} eq 'https') {
+ $protocol = $protocol{$spare_server};
+ }
+ my $alias = &Apache::lonnet::use_proxy_alias($r,$spare_server);
+ $hostname = $alias if ($alias ne '');
$spare_server = $protocol.'://'.$hostname;
}
}
@@ -948,6 +1089,103 @@ sub find_existing_session {
return;
}
+sub delusersession {
+ my ($lonid,$udom,$uname) = @_;
+ my $uprimary_id = &domain($udom,'primary');
+ my $uintdom = &internet_dom($uprimary_id);
+ my $intdom = &internet_dom($lonid);
+ my $serverhomedom = &host_domain($lonid);
+ if (($uintdom ne '') && ($uintdom eq $intdom)) {
+ return &reply(join(':','delusersession',
+ map {&escape($_)} ($udom,$uname)),$lonid);
+ }
+ return;
+}
+
+# check if user's browser sent load balancer cookie and server still has session
+# and is not overloaded.
+sub check_for_balancer_cookie {
+ my ($r,$update_mtime) = @_;
+ my ($otherserver,$cookie);
+ my %cookies=CGI::Cookie->parse($r->header_in('Cookie'));
+ if (exists($cookies{'balanceID'})) {
+ my $balid = $cookies{'balanceID'};
+ $cookie=&LONCAPA::clean_handle($balid->value);
+ my $balancedir=$r->dir_config('lonBalanceDir');
+ if ((-d $balancedir) && (-e "$balancedir/$cookie.id")) {
+ if ($cookie =~ /^($match_domain)_($match_username)_[a-f0-9]+$/) {
+ my ($possudom,$possuname) = ($1,$2);
+ my $has_session = 0;
+ if ((&domain($possudom) ne '') &&
+ (&homeserver($possuname,$possudom) ne 'no_host')) {
+ my $try_server;
+ my $opened = open(my $idf,'+<',"$balancedir/$cookie.id");
+ if ($opened) {
+ flock($idf,LOCK_SH);
+ while (my $line = <$idf>) {
+ chomp($line);
+ if (&hostname($line) ne '') {
+ $try_server = $line;
+ last;
+ }
+ }
+ close($idf);
+ if (($try_server) &&
+ (&has_user_session($try_server,$possudom,$possuname))) {
+ my $lowest_load = 30000;
+ ($otherserver,$lowest_load) =
+ &compare_server_load($try_server,undef,$lowest_load);
+ if ($otherserver ne '' && $lowest_load < 100) {
+ $has_session = 1;
+ } else {
+ undef($otherserver);
+ }
+ }
+ }
+ }
+ if ($has_session) {
+ if ($update_mtime) {
+ my $atime = my $mtime = time;
+ utime($atime,$mtime,"$balancedir/$cookie.id");
+ }
+ } else {
+ unlink("$balancedir/$cookie.id");
+ }
+ }
+ }
+ }
+ return ($otherserver,$cookie);
+}
+
+sub updatebalcookie {
+ my ($cookie,$balancer,$lastentry)=@_;
+ if ($cookie =~ /^($match_domain)\_($match_username)\_[a-f0-9]{32}$/) {
+ my ($udom,$uname) = ($1,$2);
+ my $uprimary_id = &domain($udom,'primary');
+ my $uintdom = &internet_dom($uprimary_id);
+ my $intdom = &internet_dom($balancer);
+ my $serverhomedom = &host_domain($balancer);
+ if (($uintdom ne '') && ($uintdom eq $intdom)) {
+ return &reply('updatebalcookie:'.&escape($cookie).':'.&escape($lastentry),$balancer);
+ }
+ }
+ return;
+}
+
+sub delbalcookie {
+ my ($cookie,$balancer) =@_;
+ if ($cookie =~ /^($match_domain)\_($match_username)\_[a-f0-9]{32}$/) {
+ my ($udom,$uname) = ($1,$2);
+ my $uprimary_id = &domain($udom,'primary');
+ my $uintdom = &internet_dom($uprimary_id);
+ my $intdom = &internet_dom($balancer);
+ my $serverhomedom = &host_domain($balancer);
+ if (($uintdom ne '') && ($uintdom eq $intdom)) {
+ return &reply('delbalcookie:'.&escape($cookie),$balancer);
+ }
+ }
+}
+
# -------------------------------- ask if server already has a session for user
sub has_user_session {
my ($lonid,$udom,$uname) = @_;
@@ -979,12 +1217,12 @@ sub choose_server {
}
}
foreach my $lonhost (keys(%servers)) {
- my $loginvia;
if ($skiploadbal) {
if (ref($balancers) eq 'HASH') {
next if (exists($balancers->{$lonhost}));
}
}
+ my $loginvia;
if ($checkloginvia) {
$loginvia = $domconfhash{$udom.'.login.loginvia_'.$lonhost};
if ($loginvia) {
@@ -1014,6 +1252,28 @@ sub choose_server {
return ($login_host,$hostname,$portal_path,$isredirect,$lowest_load);
}
+sub get_course_sessions {
+ my ($cnum,$cdom,$lastactivity) = @_;
+ my %servers = &internet_dom_servers($cdom);
+ my %returnhash;
+ foreach my $server (sort(keys(%servers))) {
+ my $rep = &reply("coursesessions:$cdom:$cnum:$lastactivity",$server);
+ my @pairs=split(/\&/,$rep);
+ unless (($rep eq 'unknown_cmd') || ($rep =~ /^error/)) {
+ foreach my $item (@pairs) {
+ my ($key,$value)=split(/=/,$item,2);
+ $key = &unescape($key);
+ next if ($key =~ /^error: 2 /);
+ if (exists($returnhash{$key})) {
+ next if ($value < $returnhash{$key});
+ }
+ $returnhash{$key}=$value;
+ }
+ }
+ }
+ return %returnhash;
+}
+
# --------------------------------------------- Try to change a user's password
sub changepass {
@@ -1049,6 +1309,9 @@ sub changepass {
} elsif ($answer =~ "invalid_client") {
&logthis("$server refused to change $uname in $udom password because ".
"it was a reset by e-mail originating from an invalid server.");
+ } elsif ($answer =~ "^prioruse") {
+ &logthis("$server refused to change $uname in $udom password because ".
+ "the password had been used before");
}
return $answer;
}
@@ -1187,6 +1450,15 @@ sub spare_can_host {
$canhost = 0;
}
}
+ if ($canhost) {
+ if (ref($defdomdefaults{'offloadoth'}) eq 'HASH') {
+ if ($defdomdefaults{'offloadoth'}{$try_server}) {
+ unless (&shared_institution($udom,$try_server)) {
+ $canhost = 0;
+ }
+ }
+ }
+ }
if (($canhost) && ($uint_dom)) {
my @intdoms;
my $internet_names = &get_internet_names($try_server);
@@ -1285,14 +1557,14 @@ sub get_lonbalancer_config {
sub check_loadbalancing {
my ($uname,$udom,$caller) = @_;
my ($is_balancer,$currtargets,$currrules,$dom_in_use,$homeintdom,
- $rule_in_effect,$offloadto,$otherserver);
+ $rule_in_effect,$offloadto,$otherserver,$setcookie,$dom_balancers);
my $lonhost = $perlvar{'lonHostID'};
my @hosts = ¤t_machine_ids();
my $uprimary_id = &Apache::lonnet::domain($udom,'primary');
my $uintdom = &Apache::lonnet::internet_dom($uprimary_id);
my $intdom = &Apache::lonnet::internet_dom($lonhost);
my $serverhomedom = &host_domain($lonhost);
- my $domneedscache;
+ my $domneedscache;
my $cachetime = 60*60*24;
if (($uintdom ne '') && ($uintdom eq $intdom)) {
@@ -1312,7 +1584,7 @@ sub check_loadbalancing {
}
}
if (ref($result) eq 'HASH') {
- ($is_balancer,$currtargets,$currrules) =
+ ($is_balancer,$currtargets,$currrules,$setcookie,$dom_balancers) =
&check_balancer_result($result,@hosts);
if ($is_balancer) {
if (ref($currrules) eq 'HASH') {
@@ -1373,7 +1645,7 @@ sub check_loadbalancing {
}
}
if (ref($result) eq 'HASH') {
- ($is_balancer,$currtargets,$currrules) =
+ ($is_balancer,$currtargets,$currrules,$setcookie,$dom_balancers) =
&check_balancer_result($result,@hosts);
if ($is_balancer) {
if (ref($currrules) eq 'HASH') {
@@ -1405,7 +1677,7 @@ sub check_loadbalancing {
if ($domneedscache) {
&do_cache_new('loadbalancing',$domneedscache,$is_balancer,$cachetime);
}
- if ($is_balancer) {
+ if (($is_balancer) && ($caller ne 'switchserver')) {
my $lowest_load = 30000;
if (ref($offloadto) eq 'HASH') {
if (ref($offloadto->{'primary'}) eq 'ARRAY') {
@@ -1439,7 +1711,6 @@ sub check_loadbalancing {
$is_balancer = 0;
if ($uname ne '' && $udom ne '') {
if (($env{'user.name'} eq $uname) && ($env{'user.domain'} eq $udom)) {
-
&appenv({'user.loadbalexempt' => $lonhost,
'user.loadbalcheck.time' => time});
}
@@ -1447,12 +1718,15 @@ sub check_loadbalancing {
}
}
}
- return ($is_balancer,$otherserver);
+ if (($is_balancer) && (!$homeintdom)) {
+ undef($setcookie);
+ }
+ return ($is_balancer,$otherserver,$setcookie,$offloadto,$dom_balancers);
}
sub check_balancer_result {
my ($result,@hosts) = @_;
- my ($is_balancer,$currtargets,$currrules);
+ my ($is_balancer,$currtargets,$currrules,$setcookie,$dom_balancers);
if (ref($result) eq 'HASH') {
if ($result->{'lonhost'} ne '') {
my $currbalancer = $result->{'lonhost'};
@@ -1461,19 +1735,24 @@ sub check_balancer_result {
$currtargets = $result->{'targets'};
$currrules = $result->{'rules'};
}
+ $dom_balancers = $currbalancer;
} else {
- foreach my $key (keys(%{$result})) {
- if (($key ne '') && (grep(/^\Q$key\E$/,@hosts)) &&
- (ref($result->{$key}) eq 'HASH')) {
- $is_balancer = 1;
- $currrules = $result->{$key}{'rules'};
- $currtargets = $result->{$key}{'targets'};
- last;
+ if (keys(%{$result})) {
+ foreach my $key (keys(%{$result})) {
+ if (($key ne '') && (grep(/^\Q$key\E$/,@hosts)) &&
+ (ref($result->{$key}) eq 'HASH')) {
+ $is_balancer = 1;
+ $currrules = $result->{$key}{'rules'};
+ $currtargets = $result->{$key}{'targets'};
+ $setcookie = $result->{$key}{'cookie'};
+ last;
+ }
}
+ $dom_balancers = join(',',sort(keys(%{$result})));
}
}
}
- return ($is_balancer,$currtargets,$currrules);
+ return ($is_balancer,$currtargets,$currrules,$setcookie,$dom_balancers);
}
sub get_loadbalancer_targets {
@@ -1545,6 +1824,101 @@ sub internet_dom_servers {
return %uniqservers;
}
+sub trusted_domains {
+ my ($cmdtype,$calldom) = @_;
+ my ($trusted,$untrusted);
+ if (&domain($calldom) eq '') {
+ return ($trusted,$untrusted);
+ }
+ unless ($cmdtype =~ /^(content|shared|enroll|coaurem|othcoau|domroles|catalog|reqcrs|msg)$/) {
+ return ($trusted,$untrusted);
+ }
+ my $callprimary = &domain($calldom,'primary');
+ my $intcalldom = &Apache::lonnet::internet_dom($callprimary);
+ if ($intcalldom eq '') {
+ return ($trusted,$untrusted);
+ }
+
+ my ($trustconfig,$cached)=&Apache::lonnet::is_cached_new('trust',$calldom);
+ unless (defined($cached)) {
+ my %domconfig = &Apache::lonnet::get_dom('configuration',['trust'],$calldom);
+ &Apache::lonnet::do_cache_new('trust',$calldom,$domconfig{'trust'},3600);
+ $trustconfig = $domconfig{'trust'};
+ }
+ if (ref($trustconfig)) {
+ my (%possexc,%possinc,@allexc,@allinc);
+ if (ref($trustconfig->{$cmdtype}) eq 'HASH') {
+ if (ref($trustconfig->{$cmdtype}->{'exc'}) eq 'ARRAY') {
+ map { $possexc{$_} = 1; } @{$trustconfig->{$cmdtype}->{'exc'}};
+ }
+ if (ref($trustconfig->{$cmdtype}->{'inc'}) eq 'ARRAY') {
+ $possinc{$intcalldom} = 1;
+ map { $possinc{$_} = 1; } @{$trustconfig->{$cmdtype}->{'inc'}};
+ }
+ }
+ if (keys(%possexc)) {
+ if (keys(%possinc)) {
+ foreach my $key (sort(keys(%possexc))) {
+ next if ($key eq $intcalldom);
+ unless ($possinc{$key}) {
+ push(@allexc,$key);
+ }
+ }
+ } else {
+ @allexc = sort(keys(%possexc));
+ }
+ }
+ if (keys(%possinc)) {
+ $possinc{$intcalldom} = 1;
+ @allinc = sort(keys(%possinc));
+ }
+ if ((@allexc > 0) || (@allinc > 0)) {
+ my %doms_by_intdom;
+ my %allintdoms = &all_host_intdom();
+ my %alldoms = &all_host_domain();
+ foreach my $key (%allintdoms) {
+ if (ref($doms_by_intdom{$allintdoms{$key}}) eq 'ARRAY') {
+ unless (grep(/^\Q$alldoms{$key}\E$/,@{$doms_by_intdom{$allintdoms{$key}}})) {
+ push(@{$doms_by_intdom{$allintdoms{$key}}},$alldoms{$key});
+ }
+ } else {
+ $doms_by_intdom{$allintdoms{$key}} = [$alldoms{$key}];
+ }
+ }
+ foreach my $exc (@allexc) {
+ if (ref($doms_by_intdom{$exc}) eq 'ARRAY') {
+ push(@{$untrusted},@{$doms_by_intdom{$exc}});
+ }
+ }
+ foreach my $inc (@allinc) {
+ if (ref($doms_by_intdom{$inc}) eq 'ARRAY') {
+ push(@{$trusted},@{$doms_by_intdom{$inc}});
+ }
+ }
+ }
+ }
+ return ($trusted,$untrusted);
+}
+
+sub will_trust {
+ my ($cmdtype,$domain,$possdom) = @_;
+ return 1 if ($domain eq $possdom);
+ my ($trustedref,$untrustedref) = &trusted_domains($cmdtype,$possdom);
+ my $willtrust;
+ if ((ref($trustedref) eq 'ARRAY') && (@{$trustedref} > 0)) {
+ if (grep(/^\Q$domain\E$/,@{$trustedref})) {
+ $willtrust = 1;
+ }
+ } elsif ((ref($untrustedref) eq 'ARRAY') && (@{$untrustedref} > 0)) {
+ unless (grep(/^\Q$domain\E$/,@{$untrustedref})) {
+ $willtrust = 1;
+ }
+ } else {
+ $willtrust = 1;
+ }
+ return $willtrust;
+}
+
# ---------------------- Find the homebase for a user from domain's lib servers
my %homecache;
@@ -1570,17 +1944,33 @@ sub homeserver {
return 'no_host';
}
-# ------------------------------------- Find the usernames behind a list of IDs
+# ----- Find the usernames behind a list of student/employee IDs or clicker IDs
sub idget {
- my ($udom,@ids)=@_;
+ my ($udom,$idsref,$namespace)=@_;
my %returnhash=();
+ my @ids=();
+ if (ref($idsref) eq 'ARRAY') {
+ @ids = @{$idsref};
+ } else {
+ return %returnhash;
+ }
+ if ($namespace eq '') {
+ $namespace = 'ids';
+ }
my %servers = &get_servers($udom,'library');
foreach my $tryserver (keys(%servers)) {
my $idlist=join('&', map { &escape($_); } @ids);
- $idlist=~tr/A-Z/a-z/;
- my $reply=&reply("idget:$udom:".$idlist,$tryserver);
+ if ($namespace eq 'ids') {
+ $idlist=~tr/A-Z/a-z/;
+ }
+ my $reply;
+ if ($namespace eq 'ids') {
+ $reply=&reply("idget:$udom:".$idlist,$tryserver);
+ } else {
+ $reply=&reply("getdom:$udom:$namespace:$idlist",$tryserver);
+ }
my @answer=();
if (($reply ne 'con_lost') && ($reply!~/^error\:/)) {
@answer=split(/\&/,$reply);
@@ -1589,9 +1979,9 @@ sub idget {
for ($i=0;$i<=$#ids;$i++) {
if ($answer[$i]) {
$returnhash{$ids[$i]}=&unescape($answer[$i]);
- }
+ }
}
- }
+ }
return %returnhash;
}
@@ -1606,60 +1996,141 @@ sub idrget {
return %returnhash;
}
-# ------------------------------- Store away a list of names and associated IDs
+# Store away a list of names and associated student/employee IDs or clicker IDs
sub idput {
- my ($udom,%ids)=@_;
+ my ($udom,$idsref,$uhom,$namespace)=@_;
my %servers=();
+ my %ids=();
+ my %byid = ();
+ if (ref($idsref) eq 'HASH') {
+ %ids=%{$idsref};
+ }
+ if ($namespace eq '') {
+ $namespace = 'ids';
+ }
foreach my $uname (keys(%ids)) {
&cput('environment',{'id'=>$ids{$uname}},$udom,$uname);
- my $uhom=&homeserver($uname,$udom);
+ if ($uhom eq '') {
+ $uhom=&homeserver($uname,$udom);
+ }
if ($uhom ne 'no_host') {
- my $id=&escape($ids{$uname});
- $id=~tr/A-Z/a-z/;
my $esc_unam=&escape($uname);
- if ($servers{$uhom}) {
- $servers{$uhom}.='&'.$id.'='.$esc_unam;
+ if ($namespace eq 'ids') {
+ my $id=&escape($ids{$uname});
+ $id=~tr/A-Z/a-z/;
+ my $esc_unam=&escape($uname);
+ $servers{$uhom}.=$id.'='.$esc_unam.'&';
} else {
- $servers{$uhom}=$id.'='.$esc_unam;
+ my @currids = split(/,/,$ids{$uname});
+ foreach my $id (@currids) {
+ $byid{$uhom}{$id} .= $uname.',';
+ }
+ }
+ }
+ }
+ if ($namespace eq 'clickers') {
+ foreach my $server (keys(%byid)) {
+ if (ref($byid{$server}) eq 'HASH') {
+ foreach my $id (keys(%{$byid{$server}})) {
+ $byid{$server} =~ s/,$//;
+ $servers{$uhom}.=&escape($id).'='.&escape($byid{$server}).'&';
+ }
}
}
}
foreach my $server (keys(%servers)) {
- &critical('idput:'.$udom.':'.$servers{$server},$server);
+ $servers{$server} =~ s/\&$//;
+ if ($namespace eq 'ids') {
+ &critical('idput:'.$udom.':'.$servers{$server},$server);
+ } else {
+ &critical('updateclickers:'.$udom.':add:'.$servers{$server},$server);
+ }
}
}
-# ---------------------------------------- Delete unwanted IDs from ids.db file
+# ------------- Delete unwanted student/employee IDs or clicker IDs from domain
sub iddel {
- my ($udom,$idshashref,$uhome)=@_;
+ my ($udom,$idshashref,$uhome,$namespace)=@_;
my %result=();
- unless (ref($idshashref) eq 'HASH') {
+ my %ids=();
+ my %byid = ();
+ if (ref($idshashref) eq 'HASH') {
+ %ids=%{$idshashref};
+ } else {
return %result;
}
+ if ($namespace eq '') {
+ $namespace = 'ids';
+ }
my %servers=();
- while (my ($id,$uname) = each(%{$idshashref})) {
- my $uhom;
- if ($uhome) {
- $uhom = $uhome;
- } else {
- $uhom=&homeserver($uname,$udom);
- }
- if ($uhom ne 'no_host') {
- if ($servers{$uhom}) {
+ while (my ($id,$unamestr) = each(%ids)) {
+ if ($namespace eq 'ids') {
+ my $uhom = $uhome;
+ if ($uhom eq '') {
+ $uhom=&homeserver($unamestr,$udom);
+ }
+ if ($uhom ne 'no_host') {
$servers{$uhom}.='&'.&escape($id);
- } else {
- $servers{$uhom}=&escape($id);
+ }
+ } else {
+ my @curritems = split(/,/,$ids{$id});
+ foreach my $uname (@curritems) {
+ my $uhom = $uhome;
+ if ($uhom eq '') {
+ $uhom=&homeserver($uname,$udom);
+ }
+ if ($uhom ne 'no_host') {
+ $byid{$uhom}{$id} .= $uname.',';
+ }
+ }
+ }
+ }
+ if ($namespace eq 'clickers') {
+ foreach my $server (keys(%byid)) {
+ if (ref($byid{$server}) eq 'HASH') {
+ foreach my $id (keys(%{$byid{$server}})) {
+ $byid{$server}{$id} =~ s/,$//;
+ $servers{$server}.=&escape($id).'='.&escape($byid{$server}{$id}).'&';
+ }
}
}
}
foreach my $server (keys(%servers)) {
- $result{$server} = &critical('iddel:'.$udom.':'.$servers{$server},$uhome);
+ $servers{$server} =~ s/\&$//;
+ if ($namespace eq 'ids') {
+ $result{$server} = &critical('iddel:'.$udom.':'.$servers{$server},$uhome);
+ } elsif ($namespace eq 'clickers') {
+ $result{$server} = &critical('updateclickers:'.$udom.':del:'.$servers{$server},$server);
+ }
}
return %result;
}
+# ----- Update clicker ID-to-username look-ups in clickers.db on library server
+
+sub updateclickers {
+ my ($udom,$action,$idshashref,$uhome,$critical) = @_;
+ my %clickers;
+ if (ref($idshashref) eq 'HASH') {
+ %clickers=%{$idshashref};
+ } else {
+ return;
+ }
+ my $items='';
+ foreach my $item (keys(%clickers)) {
+ $items.=&escape($item).'='.&escape($clickers{$item}).'&';
+ }
+ $items=~s/\&$//;
+ my $request = "updateclickers:$udom:$action:$items";
+ if ($critical) {
+ return &critical($request,$uhome);
+ } else {
+ return &reply($request,$uhome);
+ }
+}
+
# ------------------------------dump from db file owned by domainconfig user
sub dump_dom {
my ($namespace, $udom, $regexp) = @_;
@@ -1698,10 +2169,19 @@ sub get_dom {
}
if ($udom && $uhome && ($uhome ne 'no_host')) {
my $rep;
- if ($namespace =~ /^enc/) {
- $rep=&reply("encrypt:egetdom:$udom:$namespace:$items",$uhome);
+ if (grep { $_ eq $uhome } ¤t_machine_ids()) {
+ # domain information is hosted on this machine
+ my $cmd = 'getdom';
+ if ($namespace =~ /^enc/) {
+ $cmd = 'egetdom';
+ }
+ $rep = &LONCAPA::Lond::get_dom("$cmd:$udom:$namespace:$items");
} else {
- $rep=&reply("getdom:$udom:$namespace:$items",$uhome);
+ if ($namespace =~ /^enc/) {
+ $rep=&reply("encrypt:egetdom:$udom:$namespace:$items",$uhome);
+ } else {
+ $rep=&reply("getdom:$udom:$namespace:$items",$uhome);
+ }
}
my %returnhash;
if ($rep eq '' || $rep =~ /^error: 2 /) {
@@ -1843,11 +2323,10 @@ sub inst_directory_query {
if ($homeserver ne '') {
unless ($homeserver eq $perlvar{'lonHostID'}) {
if ($srch->{'srchby'} eq 'email') {
- my $lcrev = &get_server_loncaparev(undef,$homeserver);
- my ($major,$minor,$subver) = ($lcrev =~ /^\'?(\d+)\.(\d+)\.([\w.\-]+)\'?$/);
+ my $lcrev = &get_server_loncaparev($udom,$homeserver);
+ my ($major,$minor) = ($lcrev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
if (($major eq '' && $minor eq '') || ($major < 2) ||
- (($major == 2) && ($minor < 11)) ||
- (($major == 2) && ($minor == 11) && ($subver !~ /^2\.B/))) {
+ (($major == 2) && ($minor < 12))) {
return;
}
}
@@ -1895,11 +2374,10 @@ sub usersearch {
if (&host_domain($tryserver) eq $dom) {
unless ($tryserver eq $perlvar{'lonHostID'}) {
if ($srch->{'srchby'} eq 'email') {
- my $lcrev = &get_server_loncaparev(undef,$tryserver);
- my ($major,$minor,$subver) = ($lcrev =~ /^\'?(\d+)\.(\d+)\.([\w.\-]+)\'?$/);
+ my $lcrev = &get_server_loncaparev($dom,$tryserver);
+ my ($major,$minor) = ($lcrev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
next if (($major eq '' && $minor eq '') || ($major < 2) ||
- (($major == 2) && ($minor < 11)) ||
- (($major == 2) && ($minor == 11) && ($subver !~ /^2\.B/)));
+ (($major == 2) && ($minor < 12)));
}
}
my $host=&hostname($tryserver);
@@ -1991,7 +2469,7 @@ sub get_multiple_instusers {
my ($udom,$users,$caller) = @_;
my ($outcome,$results);
if (ref($users) eq 'HASH') {
- my $count = keys(%{$users});
+ my $count = keys(%{$users});
my $requested = &freeze_escape($users);
my $homeserver = &domain($udom,'primary');
if ($homeserver ne '') {
@@ -2035,7 +2513,7 @@ sub get_multiple_instusers {
} else {
($outcome,my $userdata) = split(/=/,$response,2);
if ($outcome eq 'ok') {
- $results = &thaw_unescape($userdata);
+ $results = &thaw_unescape($userdata);
}
}
}
@@ -2140,9 +2618,9 @@ sub get_domain_defaults {
'requestcourses','inststatus',
'coursedefaults','usersessions',
'requestauthor','selfenrollment',
- 'coursecategories','autoenroll',
- 'helpsettings'],$domain);
- my @coursetypes = ('official','unofficial','community','textbook');
+ 'coursecategories','ssl','autoenroll',
+ 'trust','helpsettings','wafproxy'],$domain);
+ my @coursetypes = ('official','unofficial','community','textbook','placement');
if (ref($domconfig{'defaults'}) eq 'HASH') {
$domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'};
$domdefaults{'auth_def'} = $domconfig{'defaults'}{'auth_def'};
@@ -2175,7 +2653,7 @@ sub get_domain_defaults {
}
}
if (ref($domconfig{'requestcourses'}) eq 'HASH') {
- foreach my $item ('official','unofficial','community','textbook') {
+ foreach my $item ('official','unofficial','community','textbook','placement') {
$domdefaults{$item} = $domconfig{'requestcourses'}{$item};
}
}
@@ -2188,6 +2666,7 @@ sub get_domain_defaults {
}
}
if (ref($domconfig{'coursedefaults'}) eq 'HASH') {
+ $domdefaults{'canuse_pdfforms'} = $domconfig{'coursedefaults'}{'canuse_pdfforms'};
$domdefaults{'usejsme'} = $domconfig{'coursedefaults'}{'usejsme'};
$domdefaults{'uselcmath'} = $domconfig{'coursedefaults'}{'uselcmath'};
if (ref($domconfig{'coursedefaults'}{'postsubmit'}) eq 'HASH') {
@@ -2204,8 +2683,8 @@ sub get_domain_defaults {
}
if ($domdefaults{'postsubmit'} eq 'on') {
if (ref($domconfig{'coursedefaults'}{'postsubmit'}{'timeout'}) eq 'HASH') {
- $domdefaults{$type.'postsubtimeout'} =
- $domconfig{'coursedefaults'}{'postsubmit'}{'timeout'}{$type};
+ $domdefaults{$type.'postsubtimeout'} =
+ $domconfig{'coursedefaults'}{'postsubmit'}{'timeout'}{$type};
}
}
}
@@ -2219,6 +2698,9 @@ sub get_domain_defaults {
} elsif ($domconfig{'coursedefaults'}{'canclone'}) {
$domdefaults{'canclone'}=$domconfig{'coursedefaults'}{'canclone'};
}
+ if ($domconfig{'coursedefaults'}{'texengine'}) {
+ $domdefaults{'texengine'} = $domconfig{'coursedefaults'}{'texengine'};
+ }
}
if (ref($domconfig{'usersessions'}) eq 'HASH') {
if (ref($domconfig{'usersessions'}{'remote'}) eq 'HASH') {
@@ -2230,6 +2712,9 @@ sub get_domain_defaults {
if (ref($domconfig{'usersessions'}{'offloadnow'}) eq 'HASH') {
$domdefaults{'offloadnow'} = $domconfig{'usersessions'}{'offloadnow'};
}
+ if (ref($domconfig{'usersessions'}{'offloadoth'}) eq 'HASH') {
+ $domdefaults{'offloadoth'} = $domconfig{'usersessions'}{'offloadoth'};
+ }
}
if (ref($domconfig{'selfenrollment'}) eq 'HASH') {
if (ref($domconfig{'selfenrollment'}{'admin'}) eq 'HASH') {
@@ -2269,6 +2754,25 @@ sub get_domain_defaults {
$domdefaults{'catunauth'} = $domconfig{'coursecategories'}{'unauth'};
}
}
+ if (ref($domconfig{'ssl'}) eq 'HASH') {
+ if (ref($domconfig{'ssl'}{'replication'}) eq 'HASH') {
+ $domdefaults{'replication'} = $domconfig{'ssl'}{'replication'};
+ }
+ if (ref($domconfig{'ssl'}{'connto'}) eq 'HASH') {
+ $domdefaults{'connect'} = $domconfig{'ssl'}{'connto'};
+ }
+ if (ref($domconfig{'ssl'}{'connfrom'}) eq 'HASH') {
+ $domdefaults{'connect'} = $domconfig{'ssl'}{'connfrom'};
+ }
+ }
+ if (ref($domconfig{'trust'}) eq 'HASH') {
+ my @prefixes = qw(content shared enroll othcoau coaurem domroles catalog reqcrs msg);
+ foreach my $prefix (@prefixes) {
+ if (ref($domconfig{'trust'}{$prefix}) eq 'HASH') {
+ $domdefaults{'trust'.$prefix} = $domconfig{'trust'}{$prefix};
+ }
+ }
+ }
if (ref($domconfig{'autoenroll'}) eq 'HASH') {
$domdefaults{'autofailsafe'} = $domconfig{'autoenroll'}{'autofailsafe'};
}
@@ -2278,12 +2782,78 @@ sub get_domain_defaults {
$domdefaults{'adhocroles'} = $domconfig{'helpsettings'}{'adhoc'};
}
}
+ if (ref($domconfig{'wafproxy'}) eq 'HASH') {
+ foreach my $item ('ipheader','trusted','vpnint','vpnext','sslopt') {
+ if ($domconfig{'wafproxy'}{$item}) {
+ $domdefaults{'waf_'.$item} = $domconfig{'wafproxy'}{$item};
+ }
+ }
+ }
&do_cache_new('domdefaults',$domain,\%domdefaults,$cachetime);
return %domdefaults;
}
+sub get_dom_cats {
+ my ($dom) = @_;
+ return unless (&domain($dom));
+ my ($cats,$cached)=&is_cached_new('cats',$dom);
+ unless (defined($cached)) {
+ my %domconfig = &get_dom('configuration',['coursecategories'],$dom);
+ if (ref($domconfig{'coursecategories'}) eq 'HASH') {
+ if (ref($domconfig{'coursecategories'}{'cats'}) eq 'HASH') {
+ %{$cats} = %{$domconfig{'coursecategories'}{'cats'}};
+ } else {
+ $cats = {};
+ }
+ } else {
+ $cats = {};
+ }
+ &Apache::lonnet::do_cache_new('cats',$dom,$cats,3600);
+ }
+ return $cats;
+}
+
+sub get_dom_instcats {
+ my ($dom) = @_;
+ return unless (&domain($dom));
+ my ($instcats,$cached)=&is_cached_new('instcats',$dom);
+ unless (defined($cached)) {
+ my (%coursecodes,%codes,@codetitles,%cat_titles,%cat_order);
+ my $totcodes = &retrieve_instcodes(\%coursecodes,$dom);
+ if ($totcodes > 0) {
+ my $caller = 'global';
+ if (&auto_instcode_format($caller,$dom,\%coursecodes,\%codes,
+ \@codetitles,\%cat_titles,\%cat_order) eq 'ok') {
+ $instcats = {
+ codes => \%codes,
+ codetitles => \@codetitles,
+ cat_titles => \%cat_titles,
+ cat_order => \%cat_order,
+ };
+ &do_cache_new('instcats',$dom,$instcats,3600);
+ }
+ }
+ }
+ return $instcats;
+}
+
+sub retrieve_instcodes {
+ my ($coursecodes,$dom) = @_;
+ my $totcodes;
+ my %courses = &courseiddump($dom,'.',1,'.','.','.',undef,undef,'Course');
+ foreach my $course (keys(%courses)) {
+ if (ref($courses{$course}) eq 'HASH') {
+ if ($courses{$course}{'inst_code'} ne '') {
+ $$coursecodes{$course} = $courses{$course}{'inst_code'};
+ $totcodes ++;
+ }
+ }
+ }
+ return $totcodes;
+}
+
sub course_portal_url {
- my ($cnum,$cdom) = @_;
+ my ($cnum,$cdom,$r) = @_;
my $chome = &homeserver($cnum,$cdom);
my $hostname = &hostname($chome);
my $protocol = $protocol{$chome};
@@ -2293,11 +2863,36 @@ sub course_portal_url {
if ($domdefaults{'portal_def'}) {
$firsturl = $domdefaults{'portal_def'};
} else {
+ my $alias = &Apache::lonnet::use_proxy_alias($r,$chome);
+ $hostname = $alias if ($alias ne '');
$firsturl = $protocol.'://'.$hostname;
}
return $firsturl;
}
+# --------------------------------------------- Get domain config for passwords
+
+sub get_passwdconf {
+ my ($dom) = @_;
+ my (%passwdconf,$gotconf,$lookup);
+ my ($result,$cached)=&is_cached_new('passwdconf',$dom);
+ if (defined($cached)) {
+ if (ref($result) eq 'HASH') {
+ %passwdconf = %{$result};
+ $gotconf = 1;
+ }
+ }
+ unless ($gotconf) {
+ my %domconfig = &get_dom('configuration',['passwords'],$dom);
+ if (ref($domconfig{'passwords'}) eq 'HASH') {
+ %passwdconf = %{$domconfig{'passwords'}};
+ }
+ my $cachetime = 24*60*60;
+ &do_cache_new('passwdconf',$dom,\%passwdconf,$cachetime);
+ }
+ return %passwdconf;
+}
+
# --------------------------------------------------- Assign a key to a student
sub assign_access_key {
@@ -2543,9 +3138,7 @@ sub devalidate_cache_new {
sub is_cached_new {
my ($name,$id,$debug) = @_;
- my $remembered_id=$name.':'.$id; # this is to avoid make_key (which is slow) for
- # keys in %remembered hash, which persists for
- # duration of request (no restriction on key length).
+ my $remembered_id=$name.':'.$id; # this is to avoid make_key (which is slow) whenever possible
if (exists($remembered{$remembered_id})) {
if ($debug) { &Apache::lonnet::logthis("Early return $remembered_id of $remembered{$remembered_id} "); }
$accessed{$remembered_id}=[&gettimeofday()];
@@ -2807,9 +3400,13 @@ sub repcopy {
mkdir($path,0777);
}
}
- my $ua=new LWP::UserAgent;
my $request=new HTTP::Request('GET',"$remoteurl");
- my $response=$ua->request($request,$transname);
+ my $response;
+ if ($remoteurl =~ m{/raw/}) {
+ $response=&LONCAPA::LWPReq::makerequest($home,$request,$transname,\%perlvar,'',0,1);
+ } else {
+ $response=&LONCAPA::LWPReq::makerequest($home,$request,$transname,\%perlvar,'',1);
+ }
if ($response->is_error()) {
unlink($transname);
my $message=$response->status_line;
@@ -2819,7 +3416,12 @@ sub repcopy {
} else {
if ($remoteurl!~/\.meta$/) {
my $mrequest=new HTTP::Request('GET',$remoteurl.'.meta');
- my $mresponse=$ua->request($mrequest,$filename.'.meta');
+ my $mresponse;
+ if ($remoteurl =~ m{/raw/}) {
+ $mresponse = &LONCAPA::LWPReq::makerequest($home,$mrequest,$filename.'.meta',\%perlvar,'',0,1);
+ } else {
+ $mresponse = &LONCAPA::LWPReq::makerequest($home,$mrequest,$filename.'.meta',\%perlvar,'',1);
+ }
if ($mresponse->is_error()) {
unlink($filename.'.meta');
&logthis(
@@ -2833,6 +3435,32 @@ sub repcopy {
}
}
+# ------------------------------------------------- Unsubscribe from a resource
+
+sub unsubscribe {
+ my ($fname) = @_;
+ my $answer;
+ if ($fname=~/\/(aboutme|syllabus|bulletinboard|smppg)$/) { return $answer; }
+ $fname=~s/[\n\r]//g;
+ my $author=$fname;
+ $author=~s/\/home\/httpd\/html\/res\/([^\/]*)\/([^\/]*).*/$1\/$2/;
+ my ($udom,$uname)=split(/\//,$author);
+ my $home=homeserver($uname,$udom);
+ if ($home eq 'no_host') {
+ $answer = 'no_host';
+ } elsif (grep { $_ eq $home } ¤t_machine_ids()) {
+ $answer = 'home';
+ } else {
+ my $defdom = $perlvar{'lonDefDomain'};
+ if (&will_trust('content',$defdom,$udom)) {
+ $answer = reply("unsub:$fname",$home);
+ } else {
+ $answer = 'untrusted';
+ }
+ }
+ return $answer;
+}
+
# ------------------------------------------------ Get server side include body
sub ssi_body {
my ($filelink,%form)=@_;
@@ -2861,11 +3489,29 @@ sub ssi_body {
# --------------------------------------------------------- Server Side Include
sub absolute_url {
- my ($host_name) = @_;
+ my ($host_name,$unalias,$keep_proto) = @_;
my $protocol = ($ENV{'SERVER_PORT'} == 443?'https://':'http://');
if ($host_name eq '') {
$host_name = $ENV{'SERVER_NAME'};
}
+ if ($unalias) {
+ my $alias = &get_proxy_alias();
+ if ($alias eq $host_name) {
+ my $lonhost = $perlvar{'lonHostID'};
+ my $hostname = &hostname($lonhost);
+ my $lcproto;
+ if (($keep_proto) || ($hostname eq '')) {
+ $lcproto = $protocol;
+ } else {
+ $lcproto = $protocol{$lonhost};
+ $lcproto = 'http' if ($lcproto ne 'https');
+ $lcproto .= '://';
+ }
+ unless ($hostname eq '') {
+ return $lcproto.$hostname;
+ }
+ }
+ }
return $protocol.$host_name;
}
@@ -2882,25 +3528,37 @@ sub absolute_url {
sub ssi {
my ($fn,%form)=@_;
- my $ua=new LWP::UserAgent;
- my $request;
+ my ($host,$request,$response);
+ $host = &absolute_url('',1);
$form{'no_update_last_known'}=1;
&Apache::lonenc::check_encrypt(\$fn);
if (%form) {
- $request=new HTTP::Request('POST',&absolute_url().$fn);
- $request->content(join('&',map {
+ $request=new HTTP::Request('POST',$host.$fn);
+ $request->content(join('&',map {
my $name = escape($_);
- "$name=" . ( ref($form{$_}) eq 'ARRAY'
- ? join("&$name=", map {escape($_) } @{$form{$_}})
- : &escape($form{$_}) );
+ "$name=" . ( ref($form{$_}) eq 'ARRAY'
+ ? join("&$name=", map {escape($_) } @{$form{$_}})
+ : &escape($form{$_}) );
} keys(%form)));
} else {
- $request=new HTTP::Request('GET',&absolute_url().$fn);
+ $request=new HTTP::Request('GET',$host.$fn);
}
$request->header(Cookie => $ENV{'HTTP_COOKIE'});
- my $response= $ua->request($request);
+ my $lonhost = $perlvar{'lonHostID'};
+ my $islocal;
+ if (($env{'request.course.id'}) &&
+ ($form{'grade_courseid'} eq $env{'request.course.id'}) &&
+ ($form{'grade_username'} ne '') && ($form{'grade_domain'} ne '') &&
+ ($form{'grade_symb'} ne '') &&
+ (&Apache::lonnet::allowed('mgr',$env{'request.course.id'}.
+ ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'')))) {
+ $islocal = 1;
+ }
+ $response= &LONCAPA::LWPReq::makerequest($lonhost,$request,'',\%perlvar,
+ '','','',$islocal);
+
if (wantarray) {
return ($response->content, $response);
} else {
@@ -2910,9 +3568,8 @@ sub ssi {
sub externalssi {
my ($url)=@_;
- my $ua=new LWP::UserAgent;
my $request=new HTTP::Request('GET',$url);
- my $response=$ua->request($request);
+ my $response = &LONCAPA::LWPReq::makerequest('',$request,'',\%perlvar);
if (wantarray) {
return ($response->content, $response);
} else {
@@ -2920,6 +3577,77 @@ sub externalssi {
}
}
+
+# If the local copy of a replicated resource is outdated, trigger a
+# connection from the homeserver to flush the delayed queue. If no update
+# happens, remove local copies of outdated resource (and corresponding
+# metadata file).
+
+sub remove_stale_resfile {
+ my ($url) = @_;
+ my $removed;
+ if ($url=~m{^/res/($match_domain)/($match_username)/}) {
+ my $audom = $1;
+ my $auname = $2;
+ unless (($url =~ /\.\d+\.\w+$/) || ($url =~ m{^/res/lib/templates/})) {
+ my $homeserver = &homeserver($auname,$audom);
+ unless (($homeserver eq 'no_host') ||
+ (grep { $_ eq $homeserver } ¤t_machine_ids())) {
+ my $fname = &filelocation('',$url);
+ if (-e $fname) {
+ my $hostname = &hostname($homeserver);
+ if ($hostname) {
+ my $protocol = $protocol{$homeserver};
+ $protocol = 'http' if ($protocol ne 'https');
+ my $uri = &declutter($url);
+ my $request=new HTTP::Request('HEAD',$protocol.'://'.$hostname.'/raw/'.$uri);
+ my $response = &LONCAPA::LWPReq::makerequest($homeserver,$request,'',\%perlvar,5,0,1);
+ if ($response->is_success()) {
+ my $remmodtime = &HTTP::Date::str2time( $response->header('Last-modified') );
+ my $locmodtime = (stat($fname))[9];
+ if ($locmodtime < $remmodtime) {
+ my $stale;
+ my $answer = &reply('pong',$homeserver);
+ if ($answer eq $homeserver.':'.$perlvar{'lonHostID'}) {
+ sleep(0.2);
+ $locmodtime = (stat($fname))[9];
+ if ($locmodtime < $remmodtime) {
+ my $posstransfer = $fname.'.in.transfer';
+ if ((-e $posstransfer) && ($remmodtime < (stat($posstransfer))[9])) {
+ $removed = 1;
+ } else {
+ $stale = 1;
+ }
+ } else {
+ $removed = 1;
+ }
+ } else {
+ $stale = 1;
+ }
+ if ($stale) {
+ if (unlink($fname)) {
+ if ($uri!~/\.meta$/) {
+ if (-e $fname.'.meta') {
+ unlink($fname.'.meta');
+ }
+ }
+ my $unsubresult = &unsubscribe($fname);
+ unless ($unsubresult eq 'ok') {
+ &logthis("no unsub of $fname from $homeserver, reason: $unsubresult");
+ }
+ $removed = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return $removed;
+}
+
# -------------------------------- Allow a /uploaded/ URI to be vouched for
sub allowuploaded {
@@ -2936,7 +3664,7 @@ sub allowuploaded {
#
# Determine if the current user should be able to edit a particular resource,
# when viewing in course context.
-# (a) When viewing resource used to determine if "Edit" item is included in
+# (a) When viewing resource used to determine if "Edit" item is included in
# Functions.
# (b) When displaying folder contents in course editor, used to determine if
# "Edit" link will be displayed alongside resource.
@@ -2944,12 +3672,12 @@ sub allowuploaded {
# input: six args -- filename (decluttered), course number, course domain,
# url, symb (if registered) and group (if this is a group
# item -- e.g., bulletin board, group page etc.).
-# output: array of five scalars --
+# output: array of five scalars --
# $cfile -- url for file editing if editable on current server
# $home -- homeserver of resource (i.e., for author if published,
# or course if uploaded.).
# $switchserver -- 1 if server switch will be needed.
-# $forceedit -- 1 if icon/link should be to go to edit mode
+# $forceedit -- 1 if icon/link should be to go to edit mode
# $forceview -- 1 if icon/link should be to go to view mode
#
@@ -3038,7 +3766,7 @@ sub can_edit_resource {
$forceedit = 1;
}
$cfile = $resurl;
- } elsif (($resurl ne '') && (&is_on_map($resurl))) {
+ } elsif (($resurl ne '') && (&is_on_map($resurl))) {
if ($resurl =~ m{^/adm/$match_domain/$match_username/\d+/smppg|bulletinboard$}) {
$incourse = 1;
if ($env{'form.forceedit'}) {
@@ -3058,6 +3786,18 @@ sub can_edit_resource {
$forceedit = 1;
}
$cfile = $resurl;
+ } elsif (($resurl =~ m{^/ext/}) && ($symb ne '')) {
+ my ($map,$id,$res) = &decode_symb($symb);
+ if ($map =~ /\.page$/) {
+ $incourse = 1;
+ if ($env{'form.forceedit'}) {
+ $forceview = 1;
+ $cfile = $map;
+ } else {
+ $forceedit = 1;
+ $cfile = '/adm/wrapper'.$resurl;
+ }
+ }
} elsif ($resurl =~ m{^/adm/wrapper/adm/$cdom/$cnum/\d+/ext\.tool$}) {
$incourse = 1;
if ($env{'form.forceedit'}) {
@@ -3077,19 +3817,19 @@ sub can_edit_resource {
}
} elsif ($resurl eq '/res/lib/templates/simpleproblem.problem/smpedit') {
my $template = '/res/lib/templates/simpleproblem.problem';
- if (&is_on_map($template)) {
+ if (&is_on_map($template)) {
$incourse = 1;
$forceview = 1;
$cfile = $template;
}
} elsif (($resurl =~ m{^/adm/wrapper/ext/}) && ($env{'form.folderpath'} =~ /^supplemental/)) {
- $incourse = 1;
- if ($env{'form.forceedit'}) {
- $forceview = 1;
- } else {
- $forceedit = 1;
- }
- $cfile = $resurl;
+ $incourse = 1;
+ if ($env{'form.forceedit'}) {
+ $forceview = 1;
+ } else {
+ $forceedit = 1;
+ }
+ $cfile = $resurl;
} elsif (($resurl =~ m{^/adm/wrapper/adm/$cdom/$cnum/\d+/ext\.tool$}) && ($env{'form.folderpath'} =~ /^supplemental/)) {
$incourse = 1;
if ($env{'form.forceedit'}) {
@@ -3137,7 +3877,7 @@ sub can_edit_resource {
$cfile=$file;
}
}
- if (($cfile ne '') && (!$incourse || $uploaded) &&
+ if (($cfile ne '') && (!$incourse || $uploaded) &&
(($home ne '') && ($home ne 'no_host'))) {
my @ids=¤t_machine_ids();
unless (grep(/^\Q$home\E$/,@ids)) {
@@ -3164,9 +3904,9 @@ sub in_course {
if ($hideprivileged) {
my $skipuser;
my %coursehash = &coursedescription($cdom.'_'.$cnum);
- my @possdoms = ($cdom);
- if ($coursehash{'checkforpriv'}) {
- push(@possdoms,split(/,/,$coursehash{'checkforpriv'}));
+ my @possdoms = ($cdom);
+ if ($coursehash{'checkforpriv'}) {
+ push(@possdoms,split(/,/,$coursehash{'checkforpriv'}));
}
if (&privileged($uname,$udom,\@possdoms)) {
$skipuser = 1;
@@ -3352,13 +4092,21 @@ sub clean_filename {
}
# Replace spaces by underscores
$fname=~s/\s+/\_/g;
+# Transliterate non-ascii text to ascii
+ my $lang = &Apache::lonlocal::current_language();
+ $fname = &LONCAPA::transliterate::fname_to_ascii($fname,$lang);
# Replace all other weird characters by nothing
$fname=~s{[^/\w\.\-]}{}g;
# Replace all .\d. sequences with _\d. so they no longer look like version
# numbers
$fname=~s/\.(\d+)(?=\.)/_$1/g;
+# Replace three or more adjacent underscores with one for consistency
+# with loncfile::filename_check() so complete url can be extracted by
+# lonnet::decode_symb()
+ $fname=~s/_{3,}/_/g;
return $fname;
}
+
# This Function checks if an Image's dimensions exceed either $resizewidth (width)
# or $resizeheight (height) - both pixels. If so, the image is scaled to produce an
# image with the same aspect ratio as the original, but with dimensions which do
@@ -3401,13 +4149,16 @@ sub resizeImage {
# input: $formname - the contents of the file are in $env{"form.$formname"}
# the desired filename is in $env{"form.$formname.filename"}
# $context - possible values: coursedoc, existingfile, overwrite,
-# canceloverwrite, or ''.
+# canceloverwrite, scantron or ''.
# if 'coursedoc': upload to the current course
# if 'existingfile': write file to tmp/overwrites directory
# if 'canceloverwrite': delete file written to tmp/overwrites directory
# $context is passed as argument to &finishuserfileupload
# $subdir - directory in userfile to store the file into
-# $parser - instruction to parse file for objects ($parser = parse)
+# $parser - instruction to parse file for objects ($parser = parse) or
+# if context is 'scantron', $parser is hashref of csv column mapping
+# (e.g.,{ PaperID => 0, LastName => 1, FirstName => 2, ID => 3,
+# Section => 4, CODE => 5, FirstQuestion => 9 }).
# $allfiles - reference to hash for embedded objects
# $codebase - reference to hash for codebase of java objects
# $desuname - username for permanent storage of uploaded file
@@ -3430,6 +4181,14 @@ sub userfileupload {
$fname=&clean_filename($fname);
# See if there is anything left
unless ($fname) { return 'error: no uploaded file'; }
+ # If filename now begins with a . prepend unix timestamp _ milliseconds
+ if ($fname =~ /^\./) {
+ my ($s,$usec) = &gettimeofday();
+ while (length($usec) < 6) {
+ $usec = '0'.$usec;
+ }
+ $fname = $s.'_'.substr($usec,0,3).$fname;
+ }
# Files uploaded to help request form, or uploaded to "create course" page are handled differently
if ((($formname eq 'screenshot') && ($subdir eq 'helprequests')) ||
(($formname eq 'coursecreatorxml') && ($subdir eq 'batchupload')) ||
@@ -3597,7 +4356,7 @@ sub finishuserfileupload {
}
}
}
- if ($parser eq 'parse') {
+ if (($context ne 'scantron') && ($parser eq 'parse')) {
if ((ref($mimetype)) && ($$mimetype eq 'text/html')) {
my $parse_result = &extract_embedded_items($filepath.'/'.$file,
$allfiles,$codebase);
@@ -3606,6 +4365,9 @@ sub finishuserfileupload {
' for embedded media: '.$parse_result);
}
}
+ } elsif (($context eq 'scantron') && (ref($parser) eq 'HASH')) {
+ my $format = $env{'form.scantron_format'};
+ &bubblesheet_converter($docudom,$filepath.'/'.$file,$parser,$format);
}
if (($thumbwidth =~ /^\d+$/) && ($thumbheight =~ /^\d+$/)) {
my $input = $filepath.'/'.$file;
@@ -3671,7 +4433,7 @@ sub extract_embedded_items {
}
if (lc($tagname) eq 'a') {
unless (($attr->{'href'} =~ /^#/) || ($attr->{'href'} eq '')) {
- &add_filetype($allfiles,$attr->{'href'},'href');
+ &add_filetype($allfiles,$attr->{'href'},'href');
}
}
if (lc($tagname) eq 'script') {
@@ -3846,6 +4608,246 @@ sub embedded_dependency {
return;
}
+sub bubblesheet_converter {
+ my ($cdom,$fullpath,$config,$format) = @_;
+ if ((&domain($cdom) ne '') &&
+ ($fullpath =~ m{^\Q$perlvar{'lonDocRoot'}/userfiles/$cdom/\E$match_courseid/scantron_orig}) &&
+ (-e $fullpath) && (ref($config) eq 'HASH') && ($format ne '')) {
+ 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)) {
+ my %bynum = (
+ $scantronconf{CODEstart} => 'CODEstart',
+ $scantronconf{IDstart} => 'IDstart',
+ $scantronconf{PaperID} => 'PaperID',
+ $scantronconf{FirstName} => 'FirstName',
+ $scantronconf{LastName} => 'LastName',
+ $scantronconf{Qstart} => 'Qstart',
+ );
+ my @ordered;
+ foreach my $item (sort { $a <=> $b } keys(%bynum)) {
+ push(@ordered,$bynum{$item});
+ }
+ my %mapstart = (
+ CODEstart => 'CODE',
+ IDstart => 'ID',
+ PaperID => 'PaperID',
+ FirstName => 'FirstName',
+ LastName => 'LastName',
+ Qstart => 'FirstQuestion',
+ );
+ my %maplength = (
+ CODEstart => 'CODElength',
+ IDstart => 'IDlength',
+ PaperID => 'PaperIDlength',
+ FirstName => 'FirstNamelength',
+ LastName => 'LastNamelength',
+ );
+ 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)) ||
+ ($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') && ($csvoptions{'pad'})) {
+ while (length($values[$i]) < $scantronconf{$maplength{$csvbynum{$i}}}) {
+ $values[$i] = '0'.$values[$i];
+ }
+ }
+ $found{$csvbynum{$i}} = $values[$i];
+ }
+ }
+ foreach my $item (@ordered) {
+ my $currlength = 1+length($record);
+ my $numspaces = $scantronconf{$item} - $currlength;
+ if ($numspaces > 0) {
+ $record .= (' ' x $numspaces);
+ }
+ if (($mapstart{$item} ne '') && (exists($found{$mapstart{$item}}))) {
+ unless ($item eq 'Qstart') {
+ if (length($found{$mapstart{$item}}) > $scantronconf{$maplength{$item}}) {
+ $found{$mapstart{$item}} = substr($found{$mapstart{$item}},0,$scantronconf{$maplength{$item}});
+ }
+ }
+ $record .= $found{$mapstart{$item}};
+ }
+ }
+ $output .= "$record\n";
+ }
+ close($fh);
+ if ($output) {
+ if (open(my $fh,'>',$fullpath)) {
+ print $fh $output;
+ close($fh);
+ }
+ }
+ }
+ }
+ return;
+ }
+}
+
+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);
+ my %config;
+ #FIXME probably should move to XML it has already gotten a bit much now
+ foreach my $line (@lines) {
+ my ($name,$descrip)=split(/:/,$line);
+ if ($name ne $which ) { next; }
+ chomp($line);
+ my @config=split(/:/,$line);
+ $config{'name'}=$config[0];
+ $config{'description'}=$config[1];
+ $config{'CODElocation'}=$config[2];
+ $config{'CODEstart'}=$config[3];
+ $config{'CODElength'}=$config[4];
+ $config{'IDstart'}=$config[5];
+ $config{'IDlength'}=$config[6];
+ $config{'Qstart'}=$config[7];
+ $config{'Qlength'}=$config[8];
+ $config{'Qoff'}=$config[9];
+ $config{'Qon'}=$config[10];
+ $config{'PaperID'}=$config[11];
+ $config{'PaperIDlength'}=$config[12];
+ $config{'FirstName'}=$config[13];
+ $config{'FirstNamelength'}=$config[14];
+ $config{'LastName'}=$config[15];
+ $config{'LastNamelength'}=$config[16];
+ $config{'BubblesPerRow'}=$config[17];
+ last;
+ }
+ return %config;
+}
+
+sub get_scantronformat_file {
+ my ($cdom) = @_;
+ if ($cdom eq '') {
+ $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
+ }
+ my %domconfig = &get_dom('configuration',['scantron'],$cdom);
+ my $gottab = 0;
+ my @lines;
+ if (ref($domconfig{'scantron'}) eq 'HASH') {
+ if ($domconfig{'scantron'}{'scantronformat'} ne '') {
+ my $formatfile = &getfile($perlvar{'lonDocRoot'}.$domconfig{'scantron'}{'scantronformat'});
+ if ($formatfile ne '-1') {
+ @lines = split("\n",$formatfile,-1);
+ $gottab = 1;
+ }
+ }
+ }
+ if (!$gottab) {
+ my $confname = $cdom.'-domainconfig';
+ my $default = $perlvar{'lonDocRoot'}.'/res/'.$cdom.'/'.$confname.'/default.tab';
+ my $formatfile = &getfile($default);
+ if ($formatfile ne '-1') {
+ @lines = split("\n",$formatfile,-1);
+ $gottab = 1;
+ }
+ }
+ if (!$gottab) {
+ my @domains = ¤t_machine_domains();
+ if (grep(/^\Q$cdom\E$/,@domains)) {
+ if (open(my $fh,'<',$perlvar{'lonTabDir'}.'/scantronformat.tab')) {
+ @lines = <$fh>;
+ close($fh);
+ }
+ } else {
+ if (open(my $fh,'<',$perlvar{'lonTabDir'}.'/default_scantronformat.tab')) {
+ @lines = <$fh>;
+ close($fh);
+ }
+ }
+ }
+ return @lines;
+}
+
sub removeuploadedurl {
my ($url)=@_;
my (undef,undef,$udom,$uname,$fname)=split('/',$url,5);
@@ -4011,19 +5013,19 @@ sub flushcourselogs {
delete $domainrolehash{$entry};
}
foreach my $dom (keys(%domrolebuffer)) {
- my %servers;
- if (defined(&domain($dom,'primary'))) {
- my $primary=&domain($dom,'primary');
- my $hostname=&hostname($primary);
- $servers{$primary} = $hostname;
- } else {
- %servers = &get_servers($dom,'library');
- }
+ my %servers;
+ if (defined(&domain($dom,'primary'))) {
+ my $primary=&domain($dom,'primary');
+ my $hostname=&hostname($primary);
+ $servers{$primary} = $hostname;
+ } else {
+ %servers = &get_servers($dom,'library');
+ }
foreach my $tryserver (keys(%servers)) {
if (&reply('domroleput:'.$dom.':'.
- $domrolebuffer{$dom},$tryserver) eq 'ok') {
- last;
- } else {
+ $domrolebuffer{$dom},$tryserver) eq 'ok') {
+ last;
+ } else {
&logthis('Put of domain roles failed for '.$dom.' and '.$tryserver);
}
}
@@ -4072,7 +5074,11 @@ sub courseacclog {
if ($formitem =~ /^HWFILE(?:SIZE|TOOBIG)/) {
$what.=':'.$formitem.'='.$env{$key};
} elsif ($formitem !~ /^HWFILE(?:[^.]+)$/) {
- $what.=':'.$formitem.'='.$env{$key};
+ if ($formitem eq 'proctorpassword') {
+ $what.=':'.$formitem.'=' . '*' x length($env{$key});
+ } else {
+ $what.=':'.$formitem.'='.$env{$key};
+ }
}
}
}
@@ -4351,7 +5357,7 @@ sub get_my_roles {
} else {
my $possdoms = [$domain];
if (ref($roledoms) eq 'ARRAY') {
- push(@{$possdoms},@{$roledoms});
+ push(@{$possdoms},@{$roledoms});
}
if (&privileged($username,$domain,$possdoms,\@privroles)) {
if (!$nothide{$username.':'.$domain}) {
@@ -4648,16 +5654,16 @@ sub courseiddump {
if (($domfilter eq '') ||
(&host_domain($tryserver) eq $domfilter)) {
my $rep;
- if (grep { $_ eq $tryserver } ¤t_machine_ids()) {
- $rep = &LONCAPA::Lond::dump_course_id_handler(
- join(":", (&host_domain($tryserver), $sincefilter,
- &escape($descfilter), &escape($instcodefilter),
+ if (grep { $_ eq $tryserver } current_machine_ids()) {
+ $rep = LONCAPA::Lond::dump_course_id_handler(
+ join(":", (&host_domain($tryserver), $sincefilter,
+ &escape($descfilter), &escape($instcodefilter),
&escape($ownerfilter), &escape($coursefilter),
- &escape($typefilter), &escape($regexp_ok),
- $as_hash, &escape($selfenrollonly),
- &escape($catfilter), $showhidden, $caller,
- &escape($cloner), &escape($cc_clone), $cloneonly,
- &escape($createdbefore), &escape($createdafter),
+ &escape($typefilter), &escape($regexp_ok),
+ $as_hash, &escape($selfenrollonly),
+ &escape($catfilter), $showhidden, $caller,
+ &escape($cloner), &escape($cc_clone), $cloneonly,
+ &escape($createdbefore), &escape($createdafter),
&escape($creationcontext),$domcloner,$hasuniquecode,
$reqcrsdom,&escape($reqinstcode))));
} else {
@@ -4673,7 +5679,7 @@ sub courseiddump {
&escape($creationcontext).':'.$domcloner.':'.$hasuniquecode.
':'.$reqcrsdom.':'.&escape($reqinstcode),$tryserver);
}
-
+
my @pairs=split(/\&/,$rep);
foreach my $item (@pairs) {
my ($key,$value)=split(/\=/,$item,2);
@@ -4869,7 +5875,12 @@ sub set_first_access {
}
$cachedkey='';
my $firstaccess=&get_first_access($type,$symb,$map);
- if (!$firstaccess) {
+ if ($firstaccess) {
+ &logthis("First access time already set ($firstaccess) when attempting ".
+ "to set new value (type: $type, extent: $res) for $uname:$udom ".
+ "in $courseid");
+ return 'already_set';
+ } else {
my $start = time;
my $putres = &put('firstaccesstimes',{"$courseid\0$res"=>$start},
$udom,$uname);
@@ -4882,6 +5893,12 @@ sub set_first_access {
'course.'.$courseid.'.timerinterval.'.$res => $interval,
}
);
+ if (($cachedtime) && (abs($start-$cachedtime) < 5)) {
+ $cachedtimes{"$courseid\0$res"} = $start;
+ }
+ } elsif ($putres ne 'refused') {
+ &logthis("Result: $putres when attempting to set first access time ".
+ "(type: $type, extent: $res) for $uname:$udom in $courseid");
}
return $putres;
}
@@ -4889,91 +5906,6 @@ sub set_first_access {
}
}
-sub checkout {
- my ($symb,$tuname,$tudom,$tcrsid)=@_;
- my $now=time;
- my $lonhost=$perlvar{'lonHostID'};
- my $infostr=&escape(
- 'CHECKOUTTOKEN&'.
- $tuname.'&'.
- $tudom.'&'.
- $tcrsid.'&'.
- $symb.'&'.
- $now.'&'.$ENV{'REMOTE_ADDR'});
- my $token=&reply('tmpput:'.$infostr,$lonhost);
- if ($token=~/^error\:/) {
- &logthis("WARNING: ".
- "Checkout tmpput failed ".$tudom.' - '.$tuname.' - '.$symb.
- "");
- return '';
- }
-
- $token=~s/^(\d+)\_.*\_(\d+)$/$1\*$2\*$lonhost/;
- $token=~tr/a-z/A-Z/;
-
- my %infohash=('resource.0.outtoken' => $token,
- 'resource.0.checkouttime' => $now,
- 'resource.0.outremote' => $ENV{'REMOTE_ADDR'});
-
- unless (&cstore(\%infohash,$symb,$tcrsid,$tudom,$tuname) eq 'ok') {
- return '';
- } else {
- &logthis("WARNING: ".
- "Checkout cstore failed ".$tudom.' - '.$tuname.' - '.$symb.
- "");
- }
-
- if (&log($tudom,$tuname,&homeserver($tuname,$tudom),
- &escape('Checkout '.$infostr.' - '.
- $token)) ne 'ok') {
- return '';
- } else {
- &logthis("WARNING: ".
- "Checkout log failed ".$tudom.' - '.$tuname.' - '.$symb.
- "");
- }
- return $token;
-}
-
-# ------------------------------------------------------------ Check in an item
-
-sub checkin {
- my $token=shift;
- my $now=time;
- my ($ta,$tb,$lonhost)=split(/\*/,$token);
- $lonhost=~tr/A-Z/a-z/;
- my $dtoken=$ta.'_'.&hostname($lonhost).'_'.$tb;
- $dtoken=~s/\W/\_/g;
- my ($dummy,$tuname,$tudom,$tcrsid,$symb,$chtim,$rmaddr)=
- split(/\&/,&unescape(&reply('tmpget:'.$dtoken,$lonhost)));
-
- unless (($tuname) && ($tudom)) {
- &logthis('Check in '.$token.' ('.$dtoken.') failed');
- return '';
- }
-
- unless (&allowed('mgr',$tcrsid)) {
- &logthis('Check in '.$token.' ('.$dtoken.') unauthorized: '.
- $env{'user.name'}.' - '.$env{'user.domain'});
- return '';
- }
-
- my %infohash=('resource.0.intoken' => $token,
- 'resource.0.checkintime' => $now,
- 'resource.0.inremote' => $ENV{'REMOTE_ADDR'});
-
- unless (&cstore(\%infohash,$symb,$tcrsid,$tudom,$tuname) eq 'ok') {
- return '';
- }
-
- if (&log($tudom,$tuname,&homeserver($tuname,$tudom),
- &escape('Checkin - '.$token)) ne 'ok') {
- return '';
- }
-
- return ($symb,$tuname,$tudom,$tcrsid);
-}
-
# --------------------------------------------- Set Expire Date for Spreadsheet
sub expirespread {
@@ -5228,7 +6160,7 @@ sub tmpreset {
if (!$domain) { $domain=$env{'user.domain'}; }
if (!$stuname) { $stuname=$env{'user.name'}; }
if ($domain eq 'public' && $stuname eq 'public') {
- $stuname=$ENV{'REMOTE_ADDR'};
+ $stuname=&get_requestor_ip();
}
my $path=LONCAPA::tempdir();
my %hash;
@@ -5265,7 +6197,7 @@ sub tmpstore {
if (!$domain) { $domain=$env{'user.domain'}; }
if (!$stuname) { $stuname=$env{'user.name'}; }
if ($domain eq 'public' && $stuname eq 'public') {
- $stuname=$ENV{'REMOTE_ADDR'};
+ $stuname=&get_requestor_ip();
}
my $now=time;
my %hash;
@@ -5309,7 +6241,7 @@ sub tmprestore {
if (!$domain) { $domain=$env{'user.domain'}; }
if (!$stuname) { $stuname=$env{'user.name'}; }
if ($domain eq 'public' && $stuname eq 'public') {
- $stuname=$ENV{'REMOTE_ADDR'};
+ $stuname=&get_requestor_ip();
}
my %returnhash;
$namespace=~s/\//\_/g;
@@ -5365,7 +6297,7 @@ sub store {
}
if (!$home) { $home=$env{'user.home'}; }
- $$storehash{'ip'}=$ENV{'REMOTE_ADDR'};
+ $$storehash{'ip'}=&get_requestor_ip();
$$storehash{'host'}=$perlvar{'lonHostID'};
my $namevalue='';
@@ -5401,7 +6333,7 @@ sub cstore {
}
if (!$home) { $home=$env{'user.home'}; }
- $$storehash{'ip'}=$ENV{'REMOTE_ADDR'};
+ $$storehash{'ip'}=&get_requestor_ip();
$$storehash{'host'}=$perlvar{'lonHostID'};
my $namevalue='';
@@ -5567,7 +6499,7 @@ sub privileged {
my $now = time;
my $roles;
if (ref($possroles) eq 'ARRAY') {
- $roles = $possroles;
+ $roles = $possroles;
} else {
$roles = ['dc','su'];
}
@@ -5594,7 +6526,7 @@ sub privileged {
for my $role (@rolesdump{grep { ! /^rolesdef_/ } keys(%rolesdump)}) {
my ($trole, $tend, $tstart) = split(/_/, $role);
if (grep(/^\Q$trole\E$/,@{$roles})) {
- return 1 unless ($tend && $tend < $now)
+ return 1 unless ($tend && $tend < $now)
or ($tstart && $tstart > $now);
}
}
@@ -5632,7 +6564,7 @@ sub privileged_by_domain {
my ($trole,$uname,$udom,$rest) = split(/:/,$item,4);
my ($end,$start) = split(/:/,$dompersonnel{$server}{$item});
next if ($end && $end < $now);
- $privileged{$dom}{$trole}{$uname.':'.$udom} =
+ $privileged{$dom}{$trole}{$uname.':'.$udom} =
$dompersonnel{$server}{$item};
}
}
@@ -6065,7 +6997,7 @@ sub check_adhoc_privs {
my $cckey = 'user.role.'.$checkrole.'./'.$cdom.'/'.$cnum;
if ($sec) {
$cckey .= '/'.$sec;
- }
+ }
my $setprivs;
if ($env{$cckey}) {
my ($role,$where,$trolecode,$tstart,$tend,$tremark,$tstatus,$tpstart,$tpend);
@@ -6107,10 +7039,11 @@ 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,
+ 'request.course.sec' => $sec,
}
);
my $tadv=0;
@@ -6170,14 +7103,15 @@ sub unserialize {
return {} if $rep =~ /^error/;
my %returnhash=();
- foreach my $item (split(/\&/,$rep)) {
- my ($key, $value) = split(/=/, $item, 2);
- $key = unescape($key) unless $escapedkeys;
- next if $key =~ /^error: 2 /;
- $returnhash{$key} = &thaw_unescape($value);
- }
+ foreach my $item (split(/\&/,$rep)) {
+ my ($key, $value) = split(/=/, $item, 2);
+ $key = unescape($key) unless $escapedkeys;
+ next if $key =~ /^error: 2 /;
+ $returnhash{$key} = &thaw_unescape($value);
+ }
+ #return %returnhash;
return \%returnhash;
-}
+}
# see Lond::dump_with_regexp
# if $escapedkeys hash keys won't get unescaped.
@@ -6192,11 +7126,11 @@ sub dump {
} else {
$regexp='.';
}
- if (grep { $_ eq $uhome } ¤t_machine_ids()) {
+ if (grep { $_ eq $uhome } current_machine_ids()) {
# user is hosted on this machine
- my $reply = LONCAPA::Lond::dump_with_regexp(join(':', ($udomain,
+ my $reply = LONCAPA::Lond::dump_with_regexp(join(":", ($udomain,
$uname, $namespace, $regexp, $range)), $perlvar{'lonVersion'});
- return %{&unserialize($reply, $escapedkeys)};
+ return %{unserialize($reply, $escapedkeys)};
}
my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range",$uhome);
my @pairs=split(/\&/,$rep);
@@ -6204,7 +7138,8 @@ sub dump {
if (!($rep =~ /^error/ )) {
foreach my $item (@pairs) {
my ($key,$value)=split(/=/,$item,2);
- $key = &unescape($key) unless ($escapedkeys);
+ $key = unescape($key) unless $escapedkeys;
+ #$key = &unescape($key);
next if ($key =~ /^error: 2 /);
$returnhash{$key}=&thaw_unescape($value);
}
@@ -6248,7 +7183,7 @@ sub currentdump {
my $rep;
if (grep { $_ eq $uhome } current_machine_ids()) {
- $rep = LONCAPA::Lond::dump_profile_database(join(":", ($sdom, $sname,
+ $rep = LONCAPA::Lond::dump_profile_database(join(":", ($sdom, $sname,
$courseid)));
} else {
$rep = reply('currentdump:'.$sdom.':'.$sname.':'.$courseid,$uhome);
@@ -6258,7 +7193,7 @@ sub currentdump {
#
my %returnhash=();
#
- if ($rep eq 'unknown_cmd') {
+ if ($rep eq 'unknown_cmd') {
# an old lond will not know currentdump
# Do a dump and make it look like a currentdump
my @tmp = &dumpstore($courseid,$sdom,$sname,'.');
@@ -6393,7 +7328,8 @@ sub putstore {
foreach my $key (keys(%{$storehash})) {
$namevalue.=&escape($key).'='.&freeze_escape($storehash->{$key}).'&';
}
- $namevalue .= 'ip='.&escape($ENV{'REMOTE_ADDR'}).
+ my $ip = &get_requestor_ip();
+ $namevalue .= 'ip='.&escape($ip).
'&host='.&escape($perlvar{'lonHostID'}).
'&version='.$esc_v.
'&by='.&escape($env{'user.name'}.':'.$env{'user.domain'});
@@ -6509,13 +7445,13 @@ sub tmpdel {
return &reply("tmpdel:$token",$server);
}
-# ------------------------------------------------------------ get_timebased_id
+# ------------------------------------------------------------ get_timebased_id
sub get_timebased_id {
my ($prefix,$keyid,$namespace,$cdom,$cnum,$idtype,$who,$locktries,
$maxtries) = @_;
my ($newid,$error,$dellock);
- unless (($prefix =~ /^\w+$/) && ($keyid =~ /^\w+$/) && ($namespace ne '')) {
+ unless (($prefix =~ /^\w+$/) && ($keyid =~ /^\w+$/) && ($namespace ne '')) {
return ('','ok','invalid call to get suffix');
}
@@ -6529,7 +7465,7 @@ sub get_timebased_id {
if (!$maxtries) {
$maxtries = 10;
}
-
+
if (($cdom eq '') || ($cnum eq '')) {
if ($env{'request.course.id'}) {
$cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
@@ -6684,7 +7620,7 @@ sub get_portfolio_access {
if (ref($access_hash->{$ipkey}{'ip'}) eq 'ARRAY') {
if (&Apache::loncommon::check_ip_acc(join(',',@{$access_hash->{$ipkey}{'ip'}}),$clientip)) {
$allowed = 1;
- last;
+ last;
}
}
}
@@ -6878,6 +7814,8 @@ sub usertools_access {
unofficial => 1,
community => 1,
textbook => 1,
+ placement => 1,
+ lti => 1,
);
} elsif ($context eq 'requestauthor') {
%tools = (
@@ -6912,7 +7850,7 @@ sub usertools_access {
my ($toolstatus,$inststatus,$envkey);
if ($context eq 'requestauthor') {
- $envkey = $context;
+ $envkey = $context;
} else {
$envkey = $context.'.'.$tool;
}
@@ -7074,25 +8012,29 @@ sub is_advanced_user {
}
sub check_can_request {
- my ($dom,$can_request,$request_domains) = @_;
+ my ($dom,$can_request,$request_domains,$uname,$udom) = @_;
my $canreq = 0;
+ if (($env{'user.name'} ne '') && ($env{'user.domain'} ne '')) {
+ $uname = $env{'user.name'};
+ $udom = $env{'user.domain'};
+ }
my ($types,$typename) = &Apache::loncommon::course_types();
my @options = ('approval','validate','autolimit');
my $optregex = join('|',@options);
if ((ref($can_request) eq 'HASH') && (ref($types) eq 'ARRAY')) {
foreach my $type (@{$types}) {
- if (&usertools_access($env{'user.name'},
- $env{'user.domain'},
- $type,undef,'requestcourses')) {
+ if (&usertools_access($uname,$udom,$type,undef,
+ 'requestcourses')) {
$canreq ++;
if (ref($request_domains) eq 'HASH') {
- push(@{$request_domains->{$type}},$env{'user.domain'});
+ push(@{$request_domains->{$type}},$udom);
}
- if ($dom eq $env{'user.domain'}) {
+ if ($dom eq $udom) {
$can_request->{$type} = 1;
}
}
- if ($env{'environment.reqcrsotherdom.'.$type} ne '') {
+ if (($env{'user.name'} ne '') && ($env{'user.domain'} ne '') &&
+ ($env{'environment.reqcrsotherdom.'.$type} ne '')) {
my @curr = split(',',$env{'environment.reqcrsotherdom.'.$type});
if (@curr > 0) {
foreach my $item (@curr) {
@@ -7109,7 +8051,7 @@ sub check_can_request {
}
}
}
- unless($dom eq $env{'user.domain'}) {
+ unless ($dom eq $env{'user.domain'}) {
$canreq ++;
if (grep(/^\Q$dom\E:($optregex)(=?\d*)$/,@curr)) {
$can_request->{$type} = 1;
@@ -7174,7 +8116,7 @@ sub customaccess {
# ------------------------------------------------- Check for a user privilege
sub allowed {
- my ($priv,$uri,$symb,$role,$clientip,$noblockcheck)=@_;
+ my ($priv,$uri,$symb,$role,$clientip,$noblockcheck,$ignorecache)=@_;
my $ver_orguri=$uri;
$uri=&deversion($uri);
my $orguri=$uri;
@@ -7191,7 +8133,7 @@ sub allowed {
if (defined($env{'allowed.'.$priv})) { return $env{'allowed.'.$priv}; }
# Free bre access to adm and meta resources
- if (((($uri=~/^adm\//) && ($uri !~ m{/(?:smppg|bulletinboard|ext\.tool)$}))
+ if (((($uri=~/^adm\//) && ($uri !~ m{/(?:smppg|bulletinboard|viewclasslist|aboutme|ext\.tool)$}))
|| (($uri=~/\.meta$/) && ($uri!~m|^uploaded/|) ))
&& ($priv eq 'bre')) {
return 'F';
@@ -7239,7 +8181,10 @@ sub allowed {
# Free bre to public access
if ($priv eq 'bre') {
- my $copyright=&metadata($uri,'copyright');
+ my $copyright;
+ unless ($uri =~ /ext\.tool/) {
+ $copyright=&metadata($uri,'copyright');
+ }
if (($copyright eq 'public') && (!$env{'request.course.id'})) {
return 'F';
}
@@ -7297,8 +8242,8 @@ sub allowed {
my $adom = $1;
foreach my $key (keys(%env)) {
if ($key =~ m{^user\.role\.(ca|aa)/\Q$adom\E}) {
- my ($start,$end) = split('.',$env{$key});
- if (($now >= $start) && (!$end || $end < $now)) {
+ my ($start,$end) = split(/\./,$env{$key});
+ if (($now >= $start) && (!$end || $end > $now)) {
$ownaccess = 1;
last;
}
@@ -7310,8 +8255,8 @@ sub allowed {
foreach my $role ('ca','aa') {
if ($env{"user.role.$role./$adom/$aname"}) {
my ($start,$end) =
- split('.',$env{"user.role.$role./$adom/$aname"});
- if (($now >= $start) && (!$end || $end < $now)) {
+ split(/\./,$env{"user.role.$role./$adom/$aname"});
+ if (($now >= $start) && (!$end || $end > $now)) {
$ownaccess = 1;
last;
}
@@ -7356,8 +8301,34 @@ sub allowed {
if ($env{'user.priv.'.$env{'request.role'}.'.'.$courseuri}
=~/\Q$priv\E\&([^\:]*)/) {
- unless (($priv eq 'bro') && (!$ownaccess)) {
- $thisallowed.=$1;
+ if ($priv eq 'mip') {
+ my $rem = $1;
+ if (($uri ne '') && ($env{'request.course.id'} eq $uri) &&
+ ($env{'course.'.$env{'request.course.id'}.'.internal.courseowner'} eq $env{'user.name'}.':'.$env{'user.domain'})) {
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ if ($cdom ne '') {
+ my %passwdconf = &get_passwdconf($cdom);
+ if (ref($passwdconf{'crsownerchg'}) eq 'HASH') {
+ if (ref($passwdconf{'crsownerchg'}{'by'}) eq 'ARRAY') {
+ if (@{$passwdconf{'crsownerchg'}{'by'}}) {
+ my @inststatuses = split(':',$env{'environment.inststatus'});
+ unless (@inststatuses) {
+ @inststatuses = ('default');
+ }
+ foreach my $status (@inststatuses) {
+ if (grep(/^\Q$status\E$/,@{$passwdconf{'crsownerchg'}{'by'}})) {
+ $thisallowed.=$rem;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ unless (($priv eq 'bro') && (!$ownaccess)) {
+ $thisallowed.=$1;
+ }
}
}
@@ -7370,10 +8341,13 @@ 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);
+ my @blockers = &has_comm_blocking($priv,$symb,$uri,$ignorecache);
if (@blockers > 0) {
$thisallowed = 'B';
} else {
@@ -7390,10 +8364,13 @@ 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);
+ my @blockers = &has_comm_blocking($priv,'',$refuri,'',1);
if (@blockers > 0) {
$thisallowed = 'B';
} else {
@@ -7412,7 +8389,7 @@ sub allowed {
&& &is_portfolio_url($uri)) {
$thisallowed = &portfolio_access($uri,$clientip);
}
-
+
# Full access at system, domain or course-wide level? Exit.
if ($thisallowed=~/F/) {
return 'F';
@@ -7440,6 +8417,16 @@ sub allowed {
if ($env{'request.course.id'}) {
+# If this is modifying password (internal auth) domains must match for user and user's role.
+
+ if ($priv eq 'mip') {
+ if ($env{'user.domain'} eq $env{'request.role.domain'}) {
+ return $thisallowed;
+ } else {
+ return '';
+ }
+ }
+
$courseprivid=$env{'request.course.id'};
if ($env{'request.course.sec'}) {
$courseprivid.='/'.$env{'request.course.sec'};
@@ -7456,7 +8443,7 @@ sub allowed {
if ($noblockcheck) {
$thisallowed.=$value;
} else {
- my @blockers = &has_comm_blocking($priv,$symb,$uri);
+ my @blockers = &has_comm_blocking($priv,$symb,$uri,$ignorecache);
if (@blockers > 0) {
$thisallowed = 'B';
} else {
@@ -7469,7 +8456,7 @@ sub allowed {
$checkreferer=0;
}
}
-
+
if ($checkreferer) {
my $refuri=$env{'httpref.'.$orguri};
unless ($refuri) {
@@ -7495,10 +8482,13 @@ 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);
+ my @blockers = &has_comm_blocking($priv,'',$refuri,'',1);
if (@blockers > 0) {
$thisallowed = 'B';
} else {
@@ -7540,16 +8530,49 @@ sub allowed {
#
# Possibly locked functionality, check all courses
+# In roles.tab, L (unless locked) available for bre, pch, plc, pac and sma.
# Locks might take effect only after 10 minutes cache expiration for other
-# courses, and 2 minutes for current course
+# courses, and 2 minutes for current course, in which user has st or ta role
+# which is neither expired nor a future role (unless current course).
- my $envkey;
+ my ($needlockcheck,$now,$crsonly);
if ($thisallowed=~/L/) {
- foreach $envkey (keys(%env)) {
+ $now = time;
+ if ($priv eq 'bre') {
+ if ($uri ne '') {
+ if ($orguri =~ m{^/+res/}) {
+ if ($uri =~ m{^lib/templates/}) {
+ if ($env{'request.course.id'}) {
+ $crsonly = 1;
+ $needlockcheck = 1;
+ }
+ } else {
+ $needlockcheck = 1;
+ }
+ } elsif ($env{'request.course.id'}) {
+ my ($crsdom,$crsnum) = split('_',$env{'request.course.id'});
+ if (($uri =~ m{^(adm|uploaded|public)/$crsdom/$crsnum/}) ||
+ ($uri =~ m{^adm/$match_domain/$match_username/\d+/(smppg|bulletinboard)$})) {
+ $crsonly = 1;
+ }
+ $needlockcheck = 1;
+ }
+ }
+ } elsif (($priv eq 'pch') || ($priv eq 'plc') || ($priv eq 'pac') || ($priv eq 'sma')) {
+ $needlockcheck = 1;
+ }
+ }
+ if ($needlockcheck) {
+ foreach my $envkey (keys(%env)) {
if ($envkey=~/^user\.role\.(st|ta)\.([^\.]*)/) {
my $courseid=$2;
my $roleid=$1.'.'.$2;
$courseid=~s/^\///;
+ unless ($env{'request.role'} eq $roleid) {
+ my ($start,$end) = split(/\./,$env{$envkey});
+ next unless (($now >= $start) && (!$end || $end > $now));
+ }
+
my $expiretime=600;
if ($env{'request.role'} eq $roleid) {
$expiretime=120;
@@ -7572,7 +8595,7 @@ sub allowed {
}
if (($env{$prefix.'priv.'.$priv.'.lock.sections'}=~/\,\Q$csec\E\,/)
|| ($env{$prefix.'priv.'.$priv.'.lock.sections'} eq 'all')) {
- if ($env{'priv.'.$priv.'.lock.expire'}>time) {
+ if ($env{$prefix.'priv.'.$priv.'.lock.expire'}>time) {
&log($env{'user.domain'},$env{'user.name'},
$env{'user.home'},
'Locked by priv: '.$priv.' for '.$uri.' due to '.
@@ -7584,7 +8607,7 @@ sub allowed {
}
}
}
-
+
#
# Rest of the restrictions depend on selected course
#
@@ -7611,7 +8634,7 @@ sub allowed {
my $unamedom=$env{'user.name'}.':'.$env{'user.domain'};
if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.roles.denied'}
=~/\Q$rolecode\E/) {
- if (($priv ne 'pch') && ($priv ne 'plc')) {
+ if (($priv ne 'pch') && ($priv ne 'plc') && ($priv ne 'pac')) {
&logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.':'.
'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode.' in '.
$env{'request.course.id'});
@@ -7621,7 +8644,7 @@ sub allowed {
if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.users.denied'}
=~/\Q$unamedom\E/) {
- if (($priv ne 'pch') && ($priv ne 'plc')) {
+ if (($priv ne 'pch') && ($priv ne 'plc') && ($priv ne 'pac')) {
&logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.
'Denied by user: '.$priv.' for '.$uri.' as '.$unamedom.' in '.
$env{'request.course.id'});
@@ -7663,6 +8686,8 @@ sub allowed {
return 'A';
} elsif ($thisallowed eq 'B') {
return 'B';
+ } elsif ($thisallowed eq 'D') {
+ return 'D';
}
return 'F';
}
@@ -7679,7 +8704,7 @@ sub constructaccess {
my ($ownername,$ownerdomain,$ownerhome);
($ownerdomain,$ownername) =
- ($url=~ m{^(?:\Q$perlvar{'lonDocRoot'}\E|)/priv/($match_domain)/($match_username)(?:/|$)});
+ ($url=~ m{^(?:\Q$perlvar{'lonDocRoot'}\E|)(?:/daxepage|/daxeopen)?/priv/($match_domain)/($match_username)(?:/|$)});
# The URL does not really point to any authorspace, forget it
unless (($ownername) && ($ownerdomain)) { return ''; }
@@ -7700,6 +8725,15 @@ sub constructaccess {
$ownerhome = &homeserver($ownername,$ownerdomain);
return ($ownername,$ownerdomain,$ownerhome);
}
+ if ($env{'request.course.id'}) {
+ if (($ownername eq $env{'course.'.$env{'request.course.id'}.'.num'}) &&
+ ($ownerdomain eq $env{'course.'.$env{'request.course.id'}.'.domain'})) {
+ if (&allowed('mdc',$env{'request.course.id'})) {
+ $ownerhome = $env{'course.'.$env{'request.course.id'}.'.home'};
+ return ($ownername,$ownerdomain,$ownerhome);
+ }
+ }
+ }
}
# We don't have any access right now. If we are not possibly going to do anything about this,
@@ -7742,22 +8776,27 @@ sub constructaccess {
#
# User for whom data are being temporarily cached.
my $cacheduser='';
-# Cached blockers for this user (a hash of blocking items).
+# Course for which data are being temporarily cached.
+my $cachedcid='';
+# Cached blockers for this user (a hash of blocking items).
my %cachedblockers=();
# When the data were last cached.
my $cachedlast='';
sub load_all_blockers {
- my ($uname,$udom,$blocks)=@_;
- if (($uname ne '') && ($udom ne '')) {
+ my ($uname,$udom)=@_;
+ if (($uname ne '') && ($udom ne '')) {
if (($cacheduser eq $uname.':'.$udom) &&
+ ($cachedcid eq $env{'request.course.id'}) &&
(abs($cachedlast-time)<5)) {
return;
}
}
$cachedlast=time;
$cacheduser=$uname.':'.$udom;
- %cachedblockers = &get_commblock_resources($blocks);
+ $cachedcid=$env{'request.course.id'};
+ %cachedblockers = &get_commblock_resources();
+ return;
}
sub get_comm_blocks {
@@ -7791,7 +8830,7 @@ sub get_commblock_resources {
} else {
%commblocks = &get_comm_blocks();
}
- return %blockers unless (keys(%commblocks) > 0);
+ return %blockers unless (keys(%commblocks) > 0);
my $navmap = Apache::lonnavmaps::navmap->new();
return %blockers unless (ref($navmap));
my $now = time;
@@ -7803,7 +8842,7 @@ sub get_commblock_resources {
if (ref($commblocks{$block}{'blocks'}{'docs'}) eq 'HASH') {
if (ref($commblocks{$block}{'blocks'}{'docs'}{'maps'}) eq 'HASH') {
if (keys(%{$commblocks{$block}{'blocks'}{'docs'}{'maps'}})) {
- $blockers{$block}{maps} = $commblocks{$block}{'blocks'}{'docs'}{'maps'};
+ $blockers{$block}{maps} = $commblocks{$block}{'blocks'}{'docs'}{'maps'};
}
}
if (ref($commblocks{$block}{'blocks'}{'docs'}{'resources'}) eq 'HASH') {
@@ -7828,8 +8867,8 @@ sub get_commblock_resources {
if ($item =~ /___\d+___/) {
$type = 'resource';
@interval=&EXT("resource.0.interval",$item);
- if (ref($navmap)) {
- my $res = $navmap->getBySymb($item);
+ if (ref($navmap)) {
+ my $res = $navmap->getBySymb($item);
push(@to_test,$res);
}
} else {
@@ -7837,14 +8876,23 @@ sub get_commblock_resources {
if ($mapsymb) {
if (ref($navmap)) {
my $mapres = $navmap->getBySymb($mapsymb);
- @to_test = $mapres->retrieveResources($mapres,undef,0,0,0,1);
- foreach my $res (@to_test) {
- my $symb = $res->symb();
- next if ($symb eq $mapsymb);
- if ($symb ne '') {
- @interval=&EXT("resource.0.interval",$symb);
- if ($interval[1] eq 'map') {
- last;
+ if (ref($mapres)) {
+ my $first = $mapres->map_start();
+ my $finish = $mapres->map_finish();
+ my $it = $navmap->getIterator($first,$finish,undef,0,0);
+ if (ref($it)) {
+ my $res;
+ while ($res = $it->next(undef,1)) {
+ next unless (ref($res));
+ my $symb = $res->symb();
+ next if (($symb eq $mapsymb) || ($symb eq ''));
+ @interval=&EXT("resource.0.interval",$symb);
+ if ($interval[1] eq 'map') {
+ if ($res->answerable()) {
+ push(@to_test,$res);
+ last;
+ }
+ }
}
}
}
@@ -7853,7 +8901,7 @@ sub get_commblock_resources {
}
}
if ($interval[0] =~ /^(\d+)/) {
- my $timelimit = $1;
+ my $timelimit = $1;
my $first_access;
if ($type eq 'resource') {
$first_access=&get_first_access($interval[1],$item);
@@ -7895,59 +8943,156 @@ sub get_commblock_resources {
}
sub has_comm_blocking {
- my ($priv,$symb,$uri,$blocks) = @_;
+ my ($priv,$symb,$uri,$ignoresymbdb,$noenccheck,$blocked,$blocks) = @_;
my @blockers;
return unless ($env{'request.course.id'});
return unless ($priv eq 'bre');
return if ($env{'user.priv.'.$env{'request.role'}} =~/evb\&([^\:]*)/);
return if ($env{'request.state'} eq 'construct');
- &load_all_blockers($env{'user.name'},$env{'user.domain'},$blocks);
- return unless (keys(%cachedblockers) > 0);
+ my %blockinfo;
+ if (ref($blocks) eq 'HASH') {
+ %blockinfo = &get_commblock_resources($blocks);
+ } else {
+ &load_all_blockers($env{'user.name'},$env{'user.domain'});
+ %blockinfo = %cachedblockers;
+ }
+ return unless (keys(%blockinfo) > 0);
my (%possibles,@symbs);
if (!$symb) {
- $symb = &symbread($uri,1,1,1,\%possibles);
+ $symb = &symbread($uri,1,1,1,\%possibles,$ignoresymbdb,$noenccheck);
}
if ($symb) {
@symbs = ($symb);
- } elsif (keys(%possibles)) {
+ } elsif (keys(%possibles)) {
@symbs = keys(%possibles);
}
my $noblock;
foreach my $symb (@symbs) {
last if ($noblock);
my ($map,$resid,$resurl)=&decode_symb($symb);
- foreach my $block (keys(%cachedblockers)) {
+ foreach my $block (keys(%blockinfo)) {
if ($block =~ /^firstaccess____(.+)$/) {
my $item = $1;
- if (($item eq $map) || ($item eq $symb)) {
- $noblock = 1;
- last;
+ unless ($blocked) {
+ if (($item eq $map) || ($item eq $symb)) {
+ $noblock = 1;
+ last;
+ }
}
}
- if (ref($cachedblockers{$block}) eq 'HASH') {
- if (ref($cachedblockers{$block}{'resources'}) eq 'HASH') {
- if ($cachedblockers{$block}{'resources'}{$symb}) {
+ if (ref($blockinfo{$block}) eq 'HASH') {
+ if (ref($blockinfo{$block}{'resources'}) eq 'HASH') {
+ if ($blockinfo{$block}{'resources'}{$symb}) {
unless (grep(/^\Q$block\E$/,@blockers)) {
push(@blockers,$block);
}
}
}
- }
- if (ref($cachedblockers{$block}{'maps'}) eq 'HASH') {
- if ($cachedblockers{$block}{'maps'}{$map}) {
- unless (grep(/^\Q$block\E$/,@blockers)) {
- push(@blockers,$block);
+ if (ref($blockinfo{$block}{'maps'}) eq 'HASH') {
+ if ($blockinfo{$block}{'maps'}{$map}) {
+ unless (grep(/^\Q$block\E$/,@blockers)) {
+ push(@blockers,$block);
+ }
}
}
}
}
}
- return if ($noblock);
- return @blockers;
+ unless ($noblock) {
+ return @blockers;
+ }
+ return;
}
}
-# -------------------------------- Deversion and split uri into path an filename
+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
#
# Removes the version from a URI and
@@ -8073,9 +9218,9 @@ sub metadata_query {
my @server_list = (defined($server_array) ? @$server_array
: keys(%libserv) );
for my $server (@server_list) {
- my $domains = '';
+ my $domains = '';
if (ref($domains_hash) eq 'HASH') {
- $domains = $domains_hash->{$server};
+ $domains = $domains_hash->{$server};
}
unless ($custom or $customshow) {
my $reply=&reply("querysend:".&escape($query).':::'.&escape($domains),$server);
@@ -8208,7 +9353,7 @@ sub fetch_enrollment_query {
}
sub get_query_reply {
- my ($queryid,$sleep,$loopmax) = @_;
+ my ($queryid,$sleep,$loopmax) = @_;;
if (($sleep eq '') || ($sleep !~ /^\d+\.?\d*$/)) {
$sleep = 0.2;
}
@@ -8341,6 +9486,25 @@ sub auto_validate_instcode {
return ($outcome,$description,$defaultcredits);
}
+sub auto_validate_inst_crosslist {
+ my ($cnum,$cdom,$instcode,$inst_xlist,$coowner) = @_;
+ my ($homeserver,$response);
+ if (($cdom =~ /^$match_domain$/) && ($cnum =~ /^$match_courseid$/)) {
+ $homeserver = &homeserver($cnum,$cdom);
+ }
+ if (!defined($homeserver)) {
+ if ($cdom =~ /^$match_domain$/) {
+ $homeserver = &domain($cdom,'primary');
+ }
+ }
+ unless (($homeserver eq '') || ($homeserver eq 'no_host')) {
+ $response=&reply('autovalidateinstcrosslist:'.$cdom.':'.
+ &escape($instcode).':'.&escape($inst_xlist).':'.
+ &escape($coowner),$homeserver);
+ }
+ return $response;
+}
+
sub auto_create_password {
my ($cnum,$cdom,$authparam,$udom) = @_;
my ($homeserver,$response);
@@ -8655,7 +9819,8 @@ sub auto_crsreq_update {
':'.&escape($action).':'.&escape($ownername).':'.
&escape($ownerdomain).':'.&escape($fullname).':'.
&escape($title).':'.&escape($code).':'.
- &escape($accessstart).':'.&escape($accessend).':'.$info,$homeserver);
+ &escape($accessstart).':'.&escape($accessend).':'.$info,
+ $homeserver);
unless ($response =~ /(con_lost|error|no_such_host|refused)/) {
my @items = split(/&/,$response);
foreach my $item (@items) {
@@ -8915,6 +10080,7 @@ sub plaintext {
my %rolenames = (
Course => 'std',
Community => 'alt1',
+ Placement => 'std',
);
if ($cid ne '') {
if ($env{'course.'.$cid.'.'.$short.'.plaintext'} ne '') {
@@ -8996,7 +10162,7 @@ sub assignrole {
}
if ($refused) {
my ($cdom,$cnum) = ($cwosec =~ m{^/?($match_domain)/($match_courseid)$});
- if (!$selfenroll && $context eq 'course') {
+ if (!$selfenroll && (($context eq 'course') || ($context eq 'ltienroll' && $env{'request.lti.login'}))) {
my %crsenv;
if ($role eq 'cc' || $role eq 'co') {
%crsenv = &userenvironment($cdom,$cnum,('internal.courseowner'));
@@ -9016,8 +10182,12 @@ sub assignrole {
}
}
}
- } elsif (($selfenroll == 1) && ($role eq 'st') && ($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) {
- $refused = '';
+ } elsif (($selfenroll == 1) && ($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) {
+ if ($role eq 'st') {
+ $refused = '';
+ } elsif (($context eq 'ltienroll') && ($env{'request.lti.login'})) {
+ $refused = '';
+ }
} elsif ($context eq 'requestcourses') {
my @possroles = ('st','ta','ep','in','cc','co');
if ((grep(/^\Q$role\E$/,@possroles)) && ($env{'user.name'} ne '' && $env{'user.domain'} ne '')) {
@@ -9036,7 +10206,7 @@ sub assignrole {
}
}
} elsif ($context eq 'requestauthor') {
- if (($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'}) &&
+ if (($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'}) &&
($url eq '/'.$udom.'/') && ($role eq 'au')) {
if ($env{'environment.requestauthor'} eq 'automatic') {
$refused = '';
@@ -9044,13 +10214,13 @@ sub assignrole {
my %domdefaults = &get_domain_defaults($udom);
if (ref($domdefaults{'requestauthor'}) eq 'HASH') {
my $checkbystatus;
- if ($env{'user.adv'}) {
+ if ($env{'user.adv'}) {
my $disposition = $domdefaults{'requestauthor'}{'_LC_adv'};
if ($disposition eq 'automatic') {
$refused = '';
} elsif ($disposition eq '') {
$checkbystatus = 1;
- }
+ }
} else {
$checkbystatus = 1;
}
@@ -9138,7 +10308,7 @@ sub assignrole {
$context);
} elsif (($role eq 'ca') || ($role eq 'aa')) {
&coauthorrolelog($role,$uname,$udom,$url,$origstart,$origend,$delflag,
- $context);
+ $context);
}
if ($role eq 'cc') {
&autoupdate_coowners($url,$end,$start,$uname,$udom);
@@ -9156,11 +10326,23 @@ sub autoupdate_coowners {
if ($domdesign{$cdom.'.autoassign.co-owners'}) {
my %coursehash = &coursedescription($cdom.'_'.$cnum);
my $instcode = $coursehash{'internal.coursecode'};
+ my $xlists = $coursehash{'internal.crosslistings'};
if ($instcode ne '') {
if (($start && $start <= $now) && ($end == 0) || ($end > $now)) {
unless ($coursehash{'internal.courseowner'} eq $uname.':'.$udom) {
my ($delcoowners,@newcoowners,$putresult,$delresult,$coowners);
my ($result,$desc) = &auto_validate_instcode($cnum,$cdom,$instcode,$uname.':'.$udom);
+ unless ($result eq 'valid') {
+ if ($xlists ne '') {
+ foreach my $xlist (split(',',$xlists)) {
+ my ($inst_crosslist,$lcsec) = split(':',$xlist);
+ $result =
+ &auto_validate_inst_crosslist($cnum,$cdom,$instcode,
+ $inst_crosslist,$uname.':'.$udom);
+ last if ($result eq 'valid');
+ }
+ }
+ }
if ($result eq 'valid') {
if ($coursehash{'internal.co-owners'}) {
foreach my $coowner (split(',',$coursehash{'internal.co-owners'})) {
@@ -9173,18 +10355,16 @@ sub autoupdate_coowners {
} else {
push(@newcoowners,$uname.':'.$udom);
}
- } else {
- if ($coursehash{'internal.co-owners'}) {
- foreach my $coowner (split(',',$coursehash{'internal.co-owners'})) {
- unless ($coowner eq $uname.':'.$udom) {
- push(@newcoowners,$coowner);
- }
- }
- unless (@newcoowners > 0) {
- $delcoowners = 1;
- $coowners = '';
+ } elsif ($coursehash{'internal.co-owners'}) {
+ foreach my $coowner (split(',',$coursehash{'internal.co-owners'})) {
+ unless ($coowner eq $uname.':'.$udom) {
+ push(@newcoowners,$coowner);
}
}
+ unless (@newcoowners > 0) {
+ $delcoowners = 1;
+ $coowners = '';
+ }
}
if (@newcoowners || $delcoowners) {
&store_coowners($cdom,$cnum,$coursehash{'home'},
@@ -9237,19 +10417,35 @@ sub store_coowners {
sub modifyuserauth {
my ($udom,$uname,$umode,$upass)=@_;
my $uhome=&homeserver($uname,$udom);
- unless (&allowed('mau',$udom)) { return 'refused'; }
+ my $allowed;
+ if (&allowed('mau',$udom)) {
+ $allowed = 1;
+ } elsif (($umode eq 'internal') && ($udom eq $env{'user.domain'}) &&
+ ($env{'request.course.id'}) && (&allowed('mip',$env{'request.course.id'})) &&
+ (!$env{'course.'.$env{'request.course.id'}.'.internal.nopasswdchg'})) {
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ if (($cdom ne '') && ($cnum ne '')) {
+ my $is_owner = &is_course_owner($cdom,$cnum);
+ if ($is_owner) {
+ $allowed = 1;
+ }
+ }
+ }
+ unless ($allowed) { return 'refused'; }
&logthis('Call to modify user authentication '.$udom.', '.$uname.', '.
$umode.' by '.$env{'user.name'}.' at '.$env{'user.domain'}.
' in domain '.$env{'request.role.domain'});
my $reply=&reply('encrypt:changeuserauth:'.$udom.':'.$uname.':'.$umode.':'.
&escape($upass),$uhome);
+ my $ip = &get_requestor_ip();
&log($env{'user.domain'},$env{'user.name'},$env{'user.home'},
'Authentication changed for '.$udom.', '.$uname.', '.$umode.
- '(Remote '.$ENV{'REMOTE_ADDR'}.'): '.$reply);
+ '(Remote '.$ip.'): '.$reply);
&log($udom,,$uname,$uhome,
'Authentication changed by '.$env{'user.domain'}.', '.
$env{'user.name'}.', '.$umode.
- '(Remote '.$ENV{'REMOTE_ADDR'}.'): '.$reply);
+ '(Remote '.$ip.'): '.$reply);
unless ($reply eq 'ok') {
&logthis('Authentication mode error: '.$reply);
return 'error: '.$reply;
@@ -9283,10 +10479,14 @@ sub modifyuser {
my $newuser;
if ($uhome eq 'no_host') {
$newuser = 1;
+ unless (($umode && ($upass ne '')) || ($umode eq 'localauth') ||
+ ($umode eq 'lti')) {
+ return 'error: more information needed to create new user';
+ }
}
# ----------------------------------------------------------------- Create User
if (($uhome eq 'no_host') &&
- (($umode && $upass) || ($umode eq 'localauth'))) {
+ (($umode && $upass) || ($umode eq 'localauth') || ($umode eq 'lti'))) {
my $unhome='';
if (defined($desiredhome) && &host_domain($desiredhome) eq $udom) {
$unhome = $desiredhome;
@@ -9328,7 +10528,7 @@ sub modifyuser {
'current user id "'.$uidhash{$uname}.'".';
}
} else {
- &idput($udom,($uname => $uid));
+ &idput($udom,{$uname => $uid},$uhome,'ids');
}
}
# -------------------------------------------------------------- Add names, etc
@@ -9453,7 +10653,7 @@ sub modifystudent {
# student's environment
$uid = undef if (!$forceid);
$reply = &modify_student_enrollment($udom,$uname,$uid,$first,$middle,$last,
- $gene,$usec,$end,$start,$type,$locktype,
+ $gene,$usec,$end,$start,$type,$locktype,
$cid,$selfenroll,$context,$credits,$instsec);
return $reply;
}
@@ -9574,14 +10774,19 @@ sub writecoursepref {
sub createcourse {
my ($udom,$description,$url,$course_server,$nonstandard,$inst_code,
- $course_owner,$crstype,$cnum,$context,$category)=@_;
+ $course_owner,$crstype,$cnum,$context,$category,$callercontext)=@_;
$url=&declutter($url);
my $cid='';
if ($context eq 'requestcourses') {
my $can_create = 0;
my ($ownername,$ownerdom) = split(':',$course_owner);
if ($udom eq $ownerdom) {
- if (&usertools_access($ownername,$ownerdom,$category,undef,
+ my $reload;
+ if (($callercontext eq 'auto') &&
+ ($ownerdom eq $env{'user.domain'}) && ($ownername eq $env{'user.name'})) {
+ $reload = 'reload';
+ }
+ if (&usertools_access($ownername,$ownerdom,$category,$reload,
$context)) {
$can_create = 1;
}
@@ -9731,12 +10936,25 @@ sub is_course {
my ($cdom, $cnum) = scalar(@_) == 1 ?
($_[0] =~ /^($match_domain)_($match_courseid)$/) : @_;
- return unless $cdom and $cnum;
-
- my %courses = &courseiddump($cdom, '.', 1, '.', '.', $cnum, undef, undef,
- '.');
-
- return unless(exists($courses{$cdom.'_'.$cnum}));
+ return unless (($cdom =~ /^$match_domain$/) && ($cnum =~ /^$match_courseid$/));
+ my $uhome=&homeserver($cnum,$cdom);
+ my $iscourse;
+ if (grep { $_ eq $uhome } current_machine_ids()) {
+ $iscourse = &LONCAPA::Lond::is_course($cdom,$cnum);
+ } else {
+ my $hashid = $cdom.':'.$cnum;
+ ($iscourse,my $cached) = &is_cached_new('iscourse',$hashid);
+ unless (defined($cached)) {
+ my %courses = &courseiddump($cdom, '.', 1, '.', '.',
+ $cnum,undef,undef,'.');
+ $iscourse = 0;
+ if (exists($courses{$cdom.'_'.$cnum})) {
+ $iscourse = 1;
+ }
+ &do_cache_new('iscourse',$hashid,$iscourse,3600);
+ }
+ }
+ return unless ($iscourse);
return wantarray ? ($cdom, $cnum) : $cdom.'_'.$cnum;
}
@@ -9753,7 +10971,7 @@ sub store_userdata {
if (($uhome eq '') || ($uhome eq 'no_host')) {
$result = 'error: no_host';
} else {
- $storehash->{'ip'} = $ENV{'REMOTE_ADDR'};
+ $storehash->{'ip'} = &get_requestor_ip();
$storehash->{'host'} = $perlvar{'lonHostID'};
my $namevalue='';
@@ -9918,7 +11136,7 @@ sub files_not_in_path {
my $filename = $user."savedfiles";
my @return_files;
my $path_part;
- open(IN,'<',LONCAPA::tempdir().$filename);
+ open(IN, '<',LONCAPA::tempdir().$filename);
while (my $line = 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.
'.$space.' - '.$qualifier.' - '.$spacequalifierrest;
# ----------------------------------------------------- Cascading lookup scheme
my $symbp=$symbparm;
- my $mapp=&deversion((&decode_symb($symbp))[0]);
-
+ $mapp=&deversion((&decode_symb($symbp))[0]);
my $symbparm=$symbp.'.'.$spacequalifierrest;
+ my $recurseparm=$mapp.'___(rec).'.$spacequalifierrest;
my $mapparm=$mapp.'___(all).'.$spacequalifierrest;
-
if (($env{'user.name'} eq $uname) &&
($env{'user.domain'} eq $udom)) {
$section=$env{'request.course.sec'};
@@ -10869,17 +12401,21 @@ sub EXT {
my $seclevel=$courseid.'.['.$section.'].'.$spacequalifierrest;
my $seclevelr=$courseid.'.['.$section.'].'.$symbparm;
+ my $secleveli=$courseid.'.['.$section.'].'.$recurseparm;
my $seclevelm=$courseid.'.['.$section.'].'.$mapparm;
$courselevel=$courseid.'.'.$spacequalifierrest;
my $courselevelr=$courseid.'.'.$symbparm;
+ $courseleveli=$courseid.'.'.$recurseparm;
$courselevelm=$courseid.'.'.$mapparm;
# ----------------------------------------------------------- first, check user
- my $userreply=&resdata($uname,$udom,'user',
+ my $userreply=&resdata($uname,$udom,'user',$mapp,\$recursed,
+ \@recurseup,$courseid,'.',$spacequalifierrest,
([$courselevelr,'resource'],
[$courselevelm,'map' ],
+ [$courseleveli,'map' ],
[$courselevel, 'course' ]));
if (defined($userreply)) { return &get_reply($userreply); }
@@ -10887,15 +12423,18 @@ sub EXT {
my $coursereply;
if (@groups > 0) {
$coursereply = &check_group_parms($courseid,\@groups,$symbparm,
- $mapparm,$spacequalifierrest);
- if (defined($coursereply)) { return &get_reply($coursereply); }
+ $recurseparm,$mapparm,$spacequalifierrest,
+ $mapp,\$recursed,\@recurseup);
+ if (defined($coursereply)) { return &get_reply($coursereply); }
}
$coursereply=&resdata($env{'course.'.$courseid.'.num'},
$env{'course.'.$courseid.'.domain'},
- 'course',
+ 'course',$mapp,\$recursed,\@recurseup,
+ $courseid,'.['.$section.'].',$spacequalifierrest,
([$seclevelr, 'resource'],
[$seclevelm, 'map' ],
+ [$secleveli, 'map' ],
[$seclevel, 'course' ],
[$courselevelr,'resource']));
if (defined($coursereply)) { return &get_reply($coursereply); }
@@ -10912,8 +12451,9 @@ sub EXT {
if ($thisparm) { return &get_reply([$thisparm,'resource']); }
}
# ------------------------------------------ fourth, look in resource metadata
-
- $spacequalifierrest=~s/\./\_/;
+
+ my $what = $spacequalifierrest;
+ $what=~s/\./\_/;
my $filename;
if (!$symbparm) { $symbparm=&symbread(); }
if ($symbparm) {
@@ -10921,18 +12461,24 @@ sub EXT {
} else {
$filename=$env{'request.filename'};
}
- my $metadata=&metadata($filename,$spacequalifierrest);
+ my $toolsymb;
+ if (($filename =~ /ext\.tool$/) && ($what ne '0_gradable')) {
+ $toolsymb = $symbparm;
+ }
+ my $metadata=&metadata($filename,$what,$toolsymb);
if (defined($metadata)) { return &get_reply([$metadata,'resource']); }
- $metadata=&metadata($filename,'parameter_'.$spacequalifierrest);
+ $metadata=&metadata($filename,'parameter_'.$what,$toolsymb);
if (defined($metadata)) { return &get_reply([$metadata,'resource']); }
-# ---------------------------------------------- fourth, look in rest of course
+# ----------------------------------------------- fifth, look in rest of course
if ($symbparm && defined($courseid) &&
$courseid eq $env{'request.course.id'}) {
my $coursereply=&resdata($env{'course.'.$courseid.'.num'},
$env{'course.'.$courseid.'.domain'},
- 'course',
+ 'course',$mapp,\$recursed,\@recurseup,
+ $courseid,'.',$spacequalifierrest,
([$courselevelm,'map' ],
+ [$courseleveli,'map' ],
[$courselevel, 'course']));
if (defined($coursereply)) { return &get_reply($coursereply); }
}
@@ -10947,7 +12493,7 @@ sub EXT {
if (defined($partgeneral[0])) { return &get_reply(\@partgeneral); }
}
if ($recurse) { return undef; }
- my $pack_def=&packages_tab_default($filename,$varname);
+ my $pack_def=&packages_tab_default($filename,$varname,$toolsymb);
if (defined($pack_def)) { return &get_reply([$pack_def,'resource']); }
# ---------------------------------------------------- Any other user namespace
} elsif ($realm eq 'environment') {
@@ -10972,6 +12518,10 @@ sub EXT {
if ($space eq 'name') {
return $ENV{'SERVER_NAME'};
}
+ } elsif ($realm eq 'client') {
+ if ($space eq 'remote_addr') {
+ return &get_requestor_ip();
+ }
}
return '';
}
@@ -10989,22 +12539,51 @@ sub get_reply {
}
sub check_group_parms {
- my ($courseid,$groups,$symbparm,$mapparm,$what) = @_;
- my @groupitems = ();
- my $resultitem;
- my @levels = ([$symbparm,'resource'],[$mapparm,'map'],[$what,'course']);
+ my ($courseid,$groups,$symbparm,$recurseparm,$mapparm,$what,$mapp,
+ $recursed,$recurseupref) = @_;
+ my @levels = ([$symbparm,'resource'],[$mapparm,'map'],[$recurseparm,'map'],
+ [$what,'course']);
+ my $coursereply;
foreach my $group (@{$groups}) {
+ my @groupitems = ();
foreach my $level (@levels) {
my $item = $courseid.'.['.$group.'].'.$level->[0];
push(@groupitems,[$item,$level->[1]]);
}
+ my $coursereply = &resdata($env{'course.'.$courseid.'.num'},
+ $env{'course.'.$courseid.'.domain'},
+ 'course',$mapp,$recursed,$recurseupref,
+ $courseid,'.['.$group.'].',$what,
+ @groupitems);
+ last if (defined($coursereply));
}
- my $coursereply = &resdata($env{'course.'.$courseid.'.num'},
- $env{'course.'.$courseid.'.domain'},
- 'course',@groupitems);
return $coursereply;
}
+sub get_map_hierarchy {
+ my ($mapname,$courseid) = @_;
+ my @recurseup = ();
+ if ($mapname) {
+ if (($cachedmapkey eq $courseid) &&
+ (abs($cachedmaptime-time)<5)) {
+ if (ref($cachedmaps{$mapname}) eq 'ARRAY') {
+ return @{$cachedmaps{$mapname}};
+ }
+ }
+ my $navmap = Apache::lonnavmaps::navmap->new();
+ if (ref($navmap)) {
+ @recurseup = $navmap->recurseup_maps($mapname);
+ undef($navmap);
+ $cachedmaps{$mapname} = \@recurseup;
+ $cachedmaptime=time;
+ $cachedmapkey=$courseid;
+ }
+ }
+ return @recurseup;
+}
+
+}
+
sub sort_course_groups { # Sort groups based on defined rankings. Default is sort().
my ($courseid,@groups) = @_;
@groups = sort(@groups);
@@ -11012,11 +12591,11 @@ sub sort_course_groups { # Sort groups b
}
sub packages_tab_default {
- my ($uri,$varname)=@_;
+ my ($uri,$varname,$toolsymb)=@_;
my (undef,$part,$name)=split(/\./,$varname);
my (@extension,@specifics,$do_default);
- foreach my $package (split(/,/,&metadata($uri,'packages'))) {
+ foreach my $package (split(/,/,&metadata($uri,'packages',$toolsymb))) {
my ($pack_type,$pack_part)=split(/_/,$package,2);
if ($pack_type eq 'default') {
$do_default=1;
@@ -11085,7 +12664,7 @@ my %metaentry;
my %importedpartids;
my %importedrespids;
sub metadata {
- my ($uri,$what,$liburi,$prefix,$depthcount)=@_;
+ my ($uri,$what,$toolsymb,$liburi,$prefix,$depthcount)=@_;
$uri=&declutter($uri);
# if it is a non metadata possible uri return quickly
if (($uri eq '') ||
@@ -11109,6 +12688,65 @@ sub metadata {
my ($result,$cached)=&is_cached_new('meta',$uri);
if (defined($cached)) { return $result->{':'.$what}; }
}
+
+#
+# If the uri is for an external tool the file from
+# which metadata should be retrieved depends on whether
+# the tool had been configured to be gradable (set in the Course
+# Editor or Resource Editor).
+#
+# If a valid symb has been included as the third arg in the call
+# to &metadata() that can be used to retrieve the value of
+# parameter_0_gradable set for the resource, and included in the
+# uploaded map containing the tool. The value is retrieved via
+# &EXT(), if a valid symb is available. Otherwise the value of
+# gradable in the exttool_$marker.db file for the tool instance
+# is retrieved via &get().
+#
+# When lonuserstate::traceroute() calls lonnet::EXT() for
+# hiddenresource and encrypturl (during course initialization)
+# the map-level parameter for resource.0.gradable included in the
+# uploaded map containing the tool will not yet have been stored
+# in the user_course_parms.db file for the user's session, so in
+# this case fall back to retrieving gradable status from the
+# exttool_$marker.db file.
+#
+# In order to avoid an infinite loop, &metadata() will return
+# before a call to &EXT(), if the uri is for an external tool
+# and the $what for which metadata is being requested is
+# parameter_0_gradable or 0_gradable.
+#
+
+ if ($uri =~ /ext\.tool$/) {
+ if (($what eq 'parameter_0_gradable') || ($what eq '0_gradable')) {
+ return;
+ } else {
+ my ($checked,$use_passback);
+ if ($toolsymb ne '') {
+ (undef,undef,my $tooluri) = &decode_symb($toolsymb);
+ if (($tooluri eq $uri) && (&EXT('resource.0.gradable',$toolsymb))) {
+ $checked = 1;
+ if (&EXT('resource.0.gradable',$toolsymb) =~ /^yes$/i) {
+ $use_passback = 1;
+ }
+ }
+ }
+ unless ($checked) {
+ my ($ignore,$cdom,$cnum,$marker) = split(m{/},$uri);
+ $marker=~s/\D//g;
+ if ($marker) {
+ my %toolsettings=&get('exttool_'.$marker,['gradable'],$cdom,$cnum);
+ $use_passback = $toolsettings{'gradable'};
+ }
+ }
+ if ($use_passback) {
+ $filename = '/home/httpd/html/res/lib/templates/LTIpassback.tool';
+ } else {
+ $filename = '/home/httpd/html/res/lib/templates/LTIstandard.tool';
+ }
+ }
+ }
+
{
# Imported parts would go here
my @origfiletagids=();
@@ -11217,23 +12855,31 @@ sub metadata {
# Check metadata for imported file to
# see if it contained response items
#
+ my ($origfile,@libfilekeys);
my %currmetaentry = %metaentry;
- my $libresponseorder = &metadata($location,'responseorder');
- my $origfile;
- if ($libresponseorder ne '') {
- if ($#origfiletagids<0) {
- undef(%importedrespids);
- undef(%importedpartids);
- }
- @{$importedrespids{$importid}} = split(/\s*,\s*/,$libresponseorder);
- if (@{$importedrespids{$importid}} > 0) {
- $importedresponses = 1;
+ @libfilekeys = split(/,/,&metadata($location,'keys',undef,undef,undef,
+ $depthcount+1));
+ if (grep(/^responseorder$/,@libfilekeys)) {
+ my $libresponseorder = &metadata($location,'responseorder',undef,undef,
+ undef,$depthcount+1);
+ if ($libresponseorder ne '') {
+ if ($#origfiletagids<0) {
+ undef(%importedrespids);
+ undef(%importedpartids);
+ }
+ my @respids = split(/\s*,\s*/,$libresponseorder);
+ if (@respids) {
+ $importedrespids{$importid} = join(',',map { $importid.'_'.$_ } @respids);
+ }
+ if ($importedrespids{$importid} ne '') {
+ $importedresponses = 1;
# We need to get the original file and the imported file to get the response order correct
# Load and inspect original file
- if ($#origfiletagids<0) {
- my $origfilelocation=$perlvar{'lonDocRoot'}.&clutter($uri);
- $origfile=&getfile($origfilelocation);
- @origfiletagids=($origfile=~/<((?:\w+)response|import|part)[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs);
+ if ($#origfiletagids<0) {
+ my $origfilelocation=$perlvar{'lonDocRoot'}.&clutter($uri);
+ $origfile=&getfile($origfilelocation);
+ @origfiletagids=($origfile=~/<((?:\w+)response|import|part)[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs);
+ }
}
}
}
@@ -11241,10 +12887,7 @@ sub metadata {
# hash populated for imported library file
%metaentry = %currmetaentry;
undef(%currmetaentry);
- if ($importmode eq 'problem') {
-# Import as problem/response
- $unikey=&add_prefix_and_part($prefix,$token->[2]->{'part'});
- } elsif ($importmode eq 'part') {
+ if ($importmode eq 'part') {
# Import as part(s)
$importedparts=1;
# We need to get the original file and the imported file to get the part order correct
@@ -11259,10 +12902,23 @@ sub metadata {
@origfiletagids=($origfile=~/<(part|import)[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs);
}
}
-
-# Load and inspect imported file
- my $impfile=&getfile($location);
- my @impfilepartids=($impfile=~/Internal Server Error