--- loncom/lonnet/perl/lonnet.pm 2022/11/23 02:55:37 1.1502
+++ loncom/lonnet/perl/lonnet.pm 2025/03/19 14:44:04 1.1537
@@ -1,7 +1,7 @@
# The LearningOnline Network
# TCP networking package
#
-# $Id: lonnet.pm,v 1.1502 2022/11/23 02:55:37 raeburn Exp $
+# $Id: lonnet.pm,v 1.1537 2025/03/19 14:44:04 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -188,7 +188,11 @@ sub create_connection {
Type => SOCK_STREAM,
Timeout => 10);
return 0 if (!$client);
- print $client (join(':',$hostname,$lonid,&machine_ids($hostname),$loncaparevs{$lonid})."\n");
+ if ($loncaparevs{$lonid} =~ /^(\d+\.\d+\.[\w.]+)-\d+$/) {
+ print $client (join(':',$hostname,$lonid,$1,&machine_ids($hostname))."\n");
+ } else {
+ print $client (join(':',$hostname,$lonid,&machine_ids($hostname))."\n");
+ }
my $result = <$client>;
chomp($result);
return 1 if ($result eq 'done');
@@ -224,7 +228,7 @@ sub get_server_distarch {
}
}
my $rep = &reply('serverdistarch',$lonhost);
- unless ($rep eq 'unknown_command' || $rep eq 'no_such_host' ||
+ unless ($rep eq 'unknown_cmd' || $rep eq 'no_such_host' ||
$rep eq 'con_lost' || $rep eq 'rejected' || $rep eq 'refused' ||
$rep eq '') {
return &do_cache_new('serverdistarch',$lonhost,$rep,$cachetime);
@@ -415,6 +419,63 @@ sub remote_devalidate_cache {
return $response;
}
+sub sign_lti {
+ my ($cdom,$cnum,$crsdef,$type,$context,$url,$ltinum,$keynum,$paramsref,$inforef) = @_;
+ my $chome;
+ if (&domain($cdom) ne '') {
+ if ($crsdef) {
+ $chome = &homeserver($cnum,$cdom);
+ } else {
+ $chome = &domain($cdom,'primary');
+ }
+ }
+ if ($cdom && $chome && ($chome ne 'no_host')) {
+ if ((ref($paramsref) eq 'HASH') &&
+ (ref($inforef) eq 'HASH')) {
+ my $rep;
+ if (grep { $_ eq $chome } ¤t_machine_ids()) {
+ # domain information is hosted on this machine
+ $rep =
+ &LONCAPA::Lond::sign_lti_payload($cdom,$cnum,$crsdef,$type,
+ $context,$url,$ltinum,$keynum,
+ $perlvar{'lonVersion'},
+ $paramsref,$inforef);
+ if (ref($rep) eq 'HASH') {
+ return ('ok',$rep);
+ }
+ } else {
+ my ($escurl,$params,$info);
+ $escurl = &escape($url);
+ if (ref($paramsref) eq 'HASH') {
+ $params = &freeze_escape($paramsref);
+ }
+ if (ref($inforef) eq 'HASH') {
+ $info = &freeze_escape($inforef);
+ }
+ $rep=&reply("encrypt:signlti:$cdom:$cnum:$crsdef:$type:$context:$escurl:$ltinum:$keynum:$params:$info",$chome);
+ }
+ if (($rep eq '') || ($rep =~ /^con_lost|error|no_such_host|unknown_cmd/i)) {
+ return ();
+ } elsif (($inforef->{'respfmt'} eq 'to_post_body') ||
+ ($inforef->{'respfmt'} eq 'to_authorization_header')) {
+ return ('ok',$rep);
+ } else {
+ my %returnhash;
+ foreach my $item (split(/\&/,$rep)) {
+ my ($name,$value)=split(/\=/,$item);
+ $returnhash{&unescape($name)}=&thaw_unescape($value);
+ }
+ return('ok',\%returnhash);
+ }
+ } else {
+ return ();
+ }
+ } else {
+ return ();
+ &logthis("sign_lti failed - no homeserver and/or domain ($cdom) ($chome)");
+ }
+}
+
# -------------------------------------------------- Non-critical communication
sub subreply {
my ($cmd,$server)=@_;
@@ -2696,9 +2757,11 @@ sub get_domain_defaults {
&get_dom('configuration',['defaults','quotas',
'requestcourses','inststatus',
'coursedefaults','usersessions',
- 'requestauthor','selfenrollment',
- 'coursecategories','ssl','autoenroll',
- 'trust','helpsettings','wafproxy','ltisec'],$domain);
+ 'requestauthor','authordefaults',
+ 'selfenrollment','coursecategories',
+ 'ssl','autoenroll','trust',
+ 'helpsettings','wafproxy',
+ 'ltisec','toolsec','privacy'],$domain);
my @coursetypes = ('official','unofficial','community','textbook','placement');
if (ref($domconfig{'defaults'}) eq 'HASH') {
$domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'};
@@ -2724,7 +2787,7 @@ sub get_domain_defaults {
} else {
$domdefaults{'defaultquota'} = $domconfig{'quotas'};
}
- my @usertools = ('aboutme','blog','webdav','portfolio');
+ my @usertools = ('aboutme','blog','webdav','portfolio','portaccess');
foreach my $item (@usertools) {
if (ref($domconfig{'quotas'}{$item}) eq 'HASH') {
$domdefaults{$item} = $domconfig{'quotas'}{$item};
@@ -2742,6 +2805,17 @@ sub get_domain_defaults {
if (ref($domconfig{'requestauthor'}) eq 'HASH') {
$domdefaults{'requestauthor'} = $domconfig{'requestauthor'};
}
+ if (ref($domconfig{'authordefaults'}) eq 'HASH') {
+ foreach my $item ('nocodemirror','copyright','sourceavail','domcoordacc','editors','archive') {
+ if ($item eq 'editors') {
+ if (ref($domconfig{'authordefaults'}{'editors'}) eq 'ARRAY') {
+ $domdefaults{$item} = join(',',@{$domconfig{'authordefaults'}{'editors'}});
+ }
+ } else {
+ $domdefaults{$item} = $domconfig{'authordefaults'}{$item};
+ }
+ }
+ }
if (ref($domconfig{'inststatus'}) eq 'HASH') {
foreach my $item ('inststatustypes','inststatusorder','inststatusguest') {
$domdefaults{$item} = $domconfig{'inststatus'}{$item};
@@ -2755,6 +2829,9 @@ sub get_domain_defaults {
if (ref($domconfig{'coursedefaults'}{'postsubmit'}) eq 'HASH') {
$domdefaults{'postsubmit'} = $domconfig{'coursedefaults'}{'postsubmit'}{'client'};
}
+ if (ref($domconfig{'coursedefaults'}{'crseditors'}) eq 'ARRAY') {
+ $domdefaults{'crseditors'}=join(',',@{$domconfig{'coursedefaults'}{'crseditors'}});
+ }
foreach my $type (@coursetypes) {
if (ref($domconfig{'coursedefaults'}{'coursecredits'}) eq 'HASH') {
unless ($type eq 'community') {
@@ -2764,12 +2841,30 @@ sub get_domain_defaults {
if (ref($domconfig{'coursedefaults'}{'uploadquota'}) eq 'HASH') {
$domdefaults{$type.'quota'} = $domconfig{'coursedefaults'}{'uploadquota'}{$type};
}
+ if (ref($domconfig{'coursedefaults'}{'coursequota'}) eq 'HASH') {
+ $domdefaults{$type.'coursequota'} = $domconfig{'coursedefaults'}{'coursequota'}{$type};
+ }
if ($domdefaults{'postsubmit'} eq 'on') {
if (ref($domconfig{'coursedefaults'}{'postsubmit'}{'timeout'}) eq 'HASH') {
$domdefaults{$type.'postsubtimeout'} =
$domconfig{'coursedefaults'}{'postsubmit'}{'timeout'}{$type};
}
}
+ if (ref($domconfig{'coursedefaults'}{'domexttool'}) eq 'HASH') {
+ $domdefaults{$type.'domexttool'} = $domconfig{'coursedefaults'}{'domexttool'}{$type};
+ } else {
+ $domdefaults{$type.'domexttool'} = 1;
+ }
+ if (ref($domconfig{'coursedefaults'}{'exttool'}) eq 'HASH') {
+ $domdefaults{$type.'exttool'} = $domconfig{'coursedefaults'}{'exttool'}{$type};
+ } else {
+ $domdefaults{$type.'exttool'} = 0;
+ }
+ if (ref($domconfig{'coursedefaults'}{'crsauthor'}) eq 'HASH') {
+ $domdefaults{$type.'crsauthor'} = $domconfig{'coursedefaults'}{'crsauthor'}{$type};
+ } else {
+ $domdefaults{$type.'crsauthor'} = 1;
+ }
}
if (ref($domconfig{'coursedefaults'}{'canclone'}) eq 'HASH') {
if (ref($domconfig{'coursedefaults'}{'canclone'}{'instcode'}) eq 'ARRAY') {
@@ -2884,10 +2979,50 @@ sub get_domain_defaults {
}
if (ref($domconfig{'ltisec'}{'private'}) eq 'HASH') {
if (ref($domconfig{'ltisec'}{'private'}{'keys'}) eq 'ARRAY') {
- $domdefaults{'privhosts'} = $domconfig{'ltisec'}{'private'}{'keys'};
+ $domdefaults{'ltiprivhosts'} = $domconfig{'ltisec'}{'private'}{'keys'};
+ }
+ }
+ if (ref($domconfig{'ltisec'}{'suggested'}) eq 'HASH') {
+ my %suggestions = %{$domconfig{'ltisec'}{'suggested'}};
+ foreach my $item (keys(%{$domconfig{'ltisec'}{'suggested'}})) {
+ unless (ref($domconfig{'ltisec'}{'suggested'}{$item}) eq 'HASH') {
+ delete($suggestions{$item});
+ }
+ }
+ if (keys(%suggestions)) {
+ $domdefaults{'linkprotsuggested'} = \%suggestions;
}
}
}
+ if (ref($domconfig{'toolsec'}) eq 'HASH') {
+ if (ref($domconfig{'toolsec'}{'encrypt'}) eq 'HASH') {
+ $domdefaults{'toolenc_crs'} = $domconfig{'toolsec'}{'encrypt'}{'crs'};
+ $domdefaults{'toolenc_dom'} = $domconfig{'toolsec'}{'encrypt'}{'dom'};
+ }
+ if (ref($domconfig{'toolsec'}{'private'}) eq 'HASH') {
+ if (ref($domconfig{'toolsec'}{'private'}{'keys'}) eq 'ARRAY') {
+ $domdefaults{'toolprivhosts'} = $domconfig{'toolsec'}{'private'}{'keys'};
+ }
+ }
+ }
+ if (ref($domconfig{'privacy'}) eq 'HASH') {
+ if (ref($domconfig{'privacy'}{'approval'}) eq 'HASH') {
+ foreach my $domtype ('instdom','extdom') {
+ if (ref($domconfig{'privacy'}{'approval'}{$domtype}) eq 'HASH') {
+ foreach my $roletype ('domain','author','course','community') {
+ if ($domconfig{'privacy'}{'approval'}{$domtype}{$roletype} eq 'user') {
+ $domdefaults{'userapprovals'} = 1;
+ last;
+ }
+ }
+ }
+ last if ($domdefaults{'userapprovals'});
+ }
+ }
+ if (ref($domconfig{'privacy'}{'othdom'}) eq 'HASH') {
+ $domdefaults{'privacyothdom'} = $domconfig{'privacy'}{'othdom'};
+ }
+ }
&do_cache_new('domdefaults',$domain,\%domdefaults,$cachetime);
return %domdefaults;
}
@@ -2924,7 +3059,7 @@ sub get_dom_instcats {
if (&auto_instcode_format($caller,$dom,\%coursecodes,\%codes,
\@codetitles,\%cat_titles,\%cat_order) eq 'ok') {
$instcats = {
- totcodes => $totcodes,
+ totcodes => $totcodes,
codes => \%codes,
codetitles => \@codetitles,
cat_titles => \%cat_titles,
@@ -3599,6 +3734,7 @@ sub ssi_body {
$output=~s|//(\s*)?\s||gs;
$output=~s/^.*?\
]*\>//si;
$output=~s/\<\/body\s*\>.*?$//si;
+ $output=~s{\Q\E}{
}gs;
if (wantarray) {
return ($output, $response);
} else {
@@ -3823,6 +3959,29 @@ sub can_edit_resource {
}
}
+#
+# For /adm/viewcoauthors can only edit if author or co-author who is manager.
+#
+
+ if (($resurl eq '/adm/viewcoauthors') && ($cnum ne '') && ($cdom ne '')) {
+ if (((&allowed('cca',"$cdom/$cnum")) ||
+ (&allowed('caa',"$cdom/$cnum"))) ||
+ ((&allowed('vca',"$cdom/$cnum") ||
+ &allowed('vaa',"$cdom/$cnum")) &&
+ ($env{"environment.internal.manager./$cdom/$cnum"}))) {
+ $home = $env{'user.home'};
+ $cfile = $resurl;
+ if ($env{'form.forceedit'}) {
+ $forceview = 1;
+ } else {
+ $forceedit = 1;
+ }
+ return ($cfile,$home,$switchserver,$forceedit,$forceview);
+ } else {
+ return;
+ }
+ }
+
if ($env{'request.course.id'}) {
my $crsedit = &allowed('mdc',$env{'request.course.id'});
if ($group ne '') {
@@ -3857,10 +4016,15 @@ sub can_edit_resource {
return;
}
} elsif (!$crsedit) {
+ if ($env{'request.role'} =~ m{^st\./$cdom/$cnum}) {
#
# No edit allowed where CC has switched to student role.
#
- return;
+ return;
+ } elsif (($resurl !~ m{^/res/$match_domain/$match_username/}) ||
+ ($resurl =~ m{^/res/lib/templates/})) {
+ return;
+ }
}
}
}
@@ -3886,7 +4050,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'}) {
@@ -4269,7 +4433,7 @@ 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, scantron or ''.
+# canceloverwrite, scantron, toollogo 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
@@ -4281,8 +4445,8 @@ sub resizeImage {
# 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
-# $dsetudom - domain for permanaent storage of uploaded file
+# $destuname - username for permanent storage of uploaded file
+# $destudom - domain for permanaent storage of uploaded file
# $thumbwidth - width (pixels) of thumbnail to make for uploaded image
# $thumbheight - height (pixels) of thumbnail to make for uploaded image
# $resizewidth - width (pixels) to which to resize uploaded image
@@ -4492,11 +4656,24 @@ sub finishuserfileupload {
if (($thumbwidth =~ /^\d+$/) && ($thumbheight =~ /^\d+$/)) {
my $input = $filepath.'/'.$file;
my $output = $filepath.'/'.'tn-'.$file;
+ my $makethumb;
my $thumbsize = $thumbwidth.'x'.$thumbheight;
- my @args = ('convert','-sample',$thumbsize,$input,$output);
- system({$args[0]} @args);
- if (-e $filepath.'/'.'tn-'.$file) {
- $fetchthumb = 1;
+ if ($context eq 'toollogo') {
+ my ($fullwidth,$fullheight) = &check_dimensions($input);
+ if ($fullwidth ne '' && $fullheight ne '') {
+ if ($fullwidth > $thumbwidth && $fullheight > $thumbheight) {
+ $makethumb = 1;
+ }
+ }
+ } else {
+ $makethumb = 1;
+ }
+ if ($makethumb) {
+ my @args = ('convert','-sample',$thumbsize,$input,$output);
+ system({$args[0]} @args);
+ if (-e $filepath.'/'.'tn-'.$file) {
+ $fetchthumb = 1;
+ }
}
}
@@ -4728,6 +4905,30 @@ sub embedded_dependency {
return;
}
+sub check_dimensions {
+ my ($inputfile) = @_;
+ my ($fullwidth,$fullheight);
+ if (($inputfile =~ m|^[/\w.\-]+$|) && (-e $inputfile)) {
+ my $mm = new File::MMagic;
+ my $mime_type = $mm->checktype_filename($inputfile);
+ if ($mime_type =~ m{^image/}) {
+ if (open(PIPE,"identify $inputfile 2>&1 |")) {
+ my $imageinfo =
;
+ if (!close(PIPE)) {
+ &Apache::lonnet::logthis("Failed to close PIPE opened to retrieve image information for $inputfile");
+ }
+ chomp($imageinfo);
+ my ($fullsize) =
+ ($imageinfo =~ /^\Q$inputfile\E\s+\w+\s+(\d+x\d+)/);
+ if ($fullsize) {
+ ($fullwidth,$fullheight) = split(/x/,$fullsize);
+ }
+ }
+ }
+ }
+ return ($fullwidth,$fullheight);
+}
+
sub bubblesheet_converter {
my ($cdom,$fullpath,$config,$format) = @_;
if ((&domain($cdom) ne '') &&
@@ -5091,7 +5292,7 @@ sub flushcourselogs {
# Typo in rev. 1.458 (2003/12/09)??
# These should likely by $env{'course.'.$cid.'.domain'} and $env{'course.'.$cid.'.num'}
#
-# While these ramain as $env{'request.'.$cid.'.domain'} and $env{'request.'.$cid.'.num'}
+# While these remain as $env{'request.'.$cid.'.domain'} and $env{'request.'.$cid.'.num'}
# $dom and $name will always be null, so the &inc() call will default to storing this data
# in a nohist_accesscount.db file for the user rather than the course.
#
@@ -5302,7 +5503,8 @@ sub userrolelog {
}
sub courserolelog {
- my ($trole,$username,$domain,$area,$tstart,$tend,$delflag,$selfenroll,$context)=@_;
+ my ($trole,$username,$domain,$area,$tstart,$tend,$delflag,$selfenroll,
+ $context,$othdomby,$requester)=@_;
if ($area =~ m-^/($match_domain)/($match_courseid)/?([^/]*)-) {
my $cdom = $1;
my $cnum = $2;
@@ -5315,11 +5517,51 @@ sub courserolelog {
selfenroll => $selfenroll,
context => $context,
);
+ if ($othdomby) {
+ if ($othdomby eq 'othdombydc') {
+ $storehash{'approval'} = 'domain';
+ } elsif ($othdomby eq 'othdombyuser') {
+ $storehash{'approval'} = 'user';
+ }
+ if ($requester ne '') {
+ $storehash{'requester'} = $requester;
+ }
+ }
if ($trole eq 'gr') {
$namespace = 'groupslog';
$storehash{'group'} = $sec;
} else {
$storehash{'section'} = $sec;
+ my ($curruserdomstr,$newuserdomstr);
+ if (exists($env{'course.'.$cdom.'_'.$cnum.'.internal.userdomains'})) {
+ $curruserdomstr = $env{'course.'.$env{'request.course.id'}.'.internal.userdomains'};
+ } else {
+ my %courseinfo = &coursedescription($cdom.'/'.$cnum);
+ $curruserdomstr = $courseinfo{'internal.userdomains'};
+ }
+ if ($curruserdomstr ne '') {
+ my @udoms = split(/,/,$curruserdomstr);
+ unless (grep(/^\Q$domain\E/,@udoms)) {
+ push(@udoms,$domain);
+ $newuserdomstr = join(',',sort(@udoms));
+ }
+ } else {
+ $newuserdomstr = $domain;
+ }
+ if ($newuserdomstr ne '') {
+ my $putresult = &put('environment',{ 'internal.userdomains' => $newuserdomstr },
+ $cdom,$cnum);
+ if ($putresult eq 'ok') {
+ unless (($selfenroll) || ($context eq 'selfenroll')) {
+ if (($context eq 'createcourse') || ($context eq 'requestcourses') ||
+ ($context eq 'automated') || ($context eq 'domain')) {
+ $env{'course.'.$cdom.'_'.$cnum.'.internal.userdomains'} = $newuserdomstr;
+ } elsif ($env{'request.course.id'} eq $cdom.'_'.$cnum) {
+ &appenv({'course.'.$cdom.'_'.$cnum.'.internal.userdomains' => $newuserdomstr});
+ }
+ }
+ }
+ }
}
&write_log('course',$namespace,\%storehash,$delflag,$username,
$domain,$cnum,$cdom);
@@ -5331,7 +5573,8 @@ sub courserolelog {
}
sub domainrolelog {
- my ($trole,$username,$domain,$area,$tstart,$tend,$delflag,$context)=@_;
+ my ($trole,$username,$domain,$area,$tstart,$tend,$delflag,
+ $context,$othdomby,$requester)=@_;
if ($area =~ m{^/($match_domain)/$}) {
my $cdom = $1;
my $domconfiguser = &get_domainconfiguser($cdom);
@@ -5342,6 +5585,16 @@ sub domainrolelog {
end => $tend,
context => $context,
);
+ if ($othdomby) {
+ if ($othdomby eq 'othdombydc') {
+ $storehash{'approval'} = 'domain';
+ } elsif ($othdomby eq 'othdombyuser') {
+ $storehash{'approval'} = 'user';
+ }
+ if ($requester ne '') {
+ $storehash{'requester'} = $requester;
+ }
+ }
&write_log('domain',$namespace,\%storehash,$delflag,$username,
$domain,$domconfiguser,$cdom);
}
@@ -5350,7 +5603,8 @@ sub domainrolelog {
}
sub coauthorrolelog {
- my ($trole,$username,$domain,$area,$tstart,$tend,$delflag,$context)=@_;
+ my ($trole,$username,$domain,$area,$tstart,$tend,$delflag,
+ $context,$othdomby,$requester)=@_;
if ($area =~ m{^/($match_domain)/($match_username)$}) {
my $audom = $1;
my $auname = $2;
@@ -5361,12 +5615,55 @@ sub coauthorrolelog {
end => $tend,
context => $context,
);
+ if ($othdomby) {
+ if ($othdomby eq 'othdombydc') {
+ $storehash{'approval'} = 'domain';
+ } elsif ($othdomby eq 'othdombyuser') {
+ $storehash{'approval'} = 'user';
+ }
+ if ($requester ne '') {
+ $storehash{'requester'} = $requester;
+ }
+ }
&write_log('author',$namespace,\%storehash,$delflag,$username,
$domain,$auname,$audom);
}
return;
}
+sub authorarchivelog {
+ my ($hashref,$size,$filesdest,$action) = @_;
+ my $lonprtdir = $Apache::lonnet::perlvar{'lonPrtDir'};
+ my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
+ $filesdest =~ s{^\Q$lonprtdir/\E}{};
+ if ($filesdest =~ m{^($match_username)_($match_domain)_archive_(\d+_\d+_\d+(|[.\w]+))$}) {
+ my ($auname,$audom,$id) = ($1,$2,$3);
+ if (ref($hashref) eq 'HASH') {
+ my $namespace = 'archivelog';
+ my $dir;
+ if ($hashref->{dir} =~ m{^\Q$londocroot/priv/$audom/$auname\E(.*)$}) {
+ $dir = $1;
+ }
+ my $delflag = 0;
+ my %storehash = (
+ id => $id,
+ dir => $dir,
+ files => $hashref->{numfiles},
+ subdirs => $hashref->{numdirs},
+ bytes => $hashref->{bytes},
+ size => $size,
+ action => $action,
+ );
+ if ($action eq 'delete') {
+ $delflag = 1;
+ }
+ &write_log('author',$namespace,\%storehash,$delflag,$auname,
+ $audom,$auname,$audom);
+ }
+ }
+ return;
+}
+
sub get_course_adv_roles {
my ($cid,$codes) = @_;
$cid=$env{'request.course.id'} unless (defined($cid));
@@ -5874,7 +6171,7 @@ sub courselastaccess {
sub extract_lastaccess {
my ($returnhash,$rep) = @_;
if (ref($returnhash) eq 'HASH') {
- unless ($rep eq 'unknown_command' || $rep eq 'no_such_host' ||
+ unless ($rep eq 'unknown_cmd' || $rep eq 'no_such_host' ||
$rep eq 'con_lost' || $rep eq 'rejected' || $rep eq 'refused' ||
$rep eq '') {
my @pairs=split(/\&/,$rep);
@@ -6461,13 +6758,17 @@ sub cstore {
if ($stuname) { $home=&homeserver($stuname,$domain); }
- $symb=&symbclean($symb);
+ unless (($symb eq '_feedback') || ($symb eq '_discussion')) {
+ $symb=&symbclean($symb);
+ }
if (!$symb) { unless ($symb=&symbread()) { return ''; } }
if (!$domain) { $domain=$env{'user.domain'}; }
if (!$stuname) { $stuname=$env{'user.name'}; }
- &devalidate($symb,$stuname,$domain);
+ unless (($symb eq '_feedback') || ($symb eq '_discussion')) {
+ &devalidate($symb,$stuname,$domain);
+ }
$symb=escape($symb);
if (!$namespace) {
@@ -6477,7 +6778,7 @@ sub cstore {
}
if (!$home) { $home=$env{'user.home'}; }
- $$storehash{'ip'}=&get_requestor_ip();
+ $$storehash{'ip'} = &get_requestor_ip();
$$storehash{'host'}=$perlvar{'lonHostID'};
my $namevalue='';
@@ -6741,7 +7042,7 @@ sub rolesinit {
my %firstaccess = &dump('firstaccesstimes', $domain, $username);
my %timerinterval = &dump('timerinterval', $domain, $username);
my (%coursetimerstarts, %firstaccchk, %firstaccenv, %coursetimerintervals,
- %timerintchk, %timerintenv);
+ %timerintchk, %timerintenv, %coauthorenv);
foreach my $key (keys(%firstaccess)) {
my ($cid, $rest) = split(/\0/, $key);
@@ -6755,6 +7056,8 @@ sub rolesinit {
my %allroles=();
my %allgroups=();
+ my %gotcoauconfig=();
+ my %domdefaults=();
for my $area (grep { ! /^rolesdef_/ } keys(%rolesdump)) {
my $role = $rolesdump{$area};
@@ -6806,6 +7109,37 @@ sub rolesinit {
} else {
# Normal role, defined in roles.tab
&standard_roleprivs(\%allroles,$trole,$tdomain,$spec,$trest,$area);
+ if (($trole eq 'ca') || ($trole eq 'aa')) {
+ (undef,my ($audom,$auname)) = split(/\//,$area);
+ unless ($gotcoauconfig{$area}) {
+ my @ca_settings = ('authoreditors','coauthorlist','coauthoroptin');
+ my %info = &userenvironment($audom,$auname,@ca_settings);
+ $gotcoauconfig{$area} = 1;
+ foreach my $item (@ca_settings) {
+ if (exists($info{$item})) {
+ my $name = $item;
+ if ($item eq 'authoreditors') {
+ $name = 'editors';
+ unless ($info{'authoreditors'}) {
+ my %domdefs;
+ if (ref($domdefaults{$audom}) eq 'HASH') {
+ %domdefs = %{$domdefaults{$audom}};
+ } else {
+ %domdefs = &get_domain_defaults($audom);
+ $domdefaults{$audom} = \%domdefs;
+ }
+ if ($domdefs{$name} ne '') {
+ $info{'authoreditors'} = $domdefs{$name};
+ } else {
+ $info{'authoreditors'} = 'edit,xml';
+ }
+ }
+ }
+ $coauthorenv{"environment.internal.$name.$area"} = $info{$item};
+ }
+ }
+ }
+ }
}
my $cid = $tdomain.'_'.$trest;
@@ -6834,7 +7168,7 @@ sub rolesinit {
$env{'user.adv'} = $userroles{'user.adv'};
$env{'user.rar'} = $userroles{'user.rar'};
- return (\%userroles,\%firstaccenv,\%timerintenv);
+ return (\%userroles,\%firstaccenv,\%timerintenv,\%coauthorenv);
}
sub set_arearole {
@@ -7194,6 +7528,27 @@ sub set_adhoc_privileges {
if (&allowed('adv') eq 'F') { $tadv=1; }
&appenv({'request.role.adv' => $tadv});
}
+ if ($role eq 'ca') {
+ my @ca_settings = ('authoreditors','coauthorlist');
+ my %info = &userenvironment($dcdom,$pickedcourse,@ca_settings);
+ foreach my $item (@ca_settings) {
+ if (exists($info{$item})) {
+ my $name = $item;
+ if ($item eq 'authoreditors') {
+ $name = 'editors';
+ unless ($info{'authoreditors'}) {
+ my %domdefs = &get_domain_defaults($dcdom);
+ if ($domdefs{$name} ne '') {
+ $info{'authoreditors'} = $domdefs{$name};
+ } else {
+ $info{'authoreditors'} = 'edit,xml';
+ }
+ }
+ }
+ &appenv({"environment.internal.$name./$dcdom/$pickedcourse" => $info{$item}});
+ }
+ }
+ }
}
# --------------------------------------------------------------- get interface
@@ -7728,7 +8083,7 @@ sub portfolio_access {
}
sub get_portfolio_access {
- my ($udom,$unum,$file_name,$group,$clientip,$access_hash) = @_;
+ my ($udom,$unum,$file_name,$group,$clientip,$access_hash,$portaccessref) = @_;
if (!ref($access_hash)) {
my $current_perms = &get_portfile_permissions($udom,$unum);
@@ -7737,11 +8092,19 @@ sub get_portfolio_access {
$access_hash = $access_controls{$file_name};
}
- my ($public,$guest,@domains,@users,@courses,@groups,@ips);
+ my $portaccess;
+ if (ref($portaccess) eq 'SCALAR') {
+ $portaccess = $$portaccessref;
+ } else {
+ $portaccess = &usertools_access($unum,$udom,'portaccess',undef,'tools');
+ }
+
+ my ($public,$guest,@domains,@users,@courses,@groups,@ips,@userips);
my $now = time;
if (ref($access_hash) eq 'HASH') {
foreach my $key (keys(%{$access_hash})) {
my ($num,$scope,$end,$start) = ($key =~ /^([^:]+):([a-z]+)_(\d*)_?(\d*)$/);
+ next if (($scope ne 'ip') && ($portaccess == 0));
if ($start > $now) {
next;
}
@@ -7763,6 +8126,8 @@ sub get_portfolio_access {
push(@groups,$key);
} elsif ($scope eq 'ip') {
push(@ips,$key);
+ } elsif ($scope eq 'userip') {
+ push(@userips,$key);
}
}
if ($public) {
@@ -7780,6 +8145,19 @@ sub get_portfolio_access {
if ($allowed) {
return 'ok';
}
+ } elsif (@userips > 0) {
+ my $allowed;
+ foreach my $useripkey (@userips) {
+ if (ref($access_hash->{$useripkey}{'ip'}) eq 'ARRAY') {
+ if (&Apache::loncommon::check_ip_acc(join(',',@{$access_hash->{$useripkey}{'ip'}}),$clientip)) {
+ $allowed = 1;
+ last;
+ }
+ }
+ }
+ if ($allowed) {
+ return 'ok';
+ }
}
if ($env{'user.name'} eq 'public' && $env{'user.domain'} eq 'public') {
if ($guest) {
@@ -7955,6 +8333,17 @@ sub is_portfolio_file {
return;
}
+sub is_coursetool_logo {
+ my ($uri) = @_;
+ if ($env{'request.course.id'}) {
+ my $courseurl = &courseid_to_courseurl($env{'request.course.id'});
+ if ($uri =~ m{^/*uploaded\Q$courseurl\E/toollogo/\d+/[^/]+$}) {
+ return 1;
+ }
+ }
+ return;
+}
+
sub usertools_access {
my ($uname,$udom,$tool,$action,$context,$userenvref,$domdefref,$is_advref)=@_;
my ($access,%tools);
@@ -7974,12 +8363,17 @@ sub usertools_access {
%tools = (
requestauthor => 1,
);
+ } elsif ($context eq 'authordefaults') {
+ %tools = (
+ webdav => 1,
+ );
} else {
%tools = (
aboutme => 1,
blog => 1,
webdav => 1,
portfolio => 1,
+ portaccess => 1,
timezone => 1,
);
}
@@ -7996,6 +8390,10 @@ sub usertools_access {
return $env{'environment.canrequest.'.$tool};
} elsif ($context eq 'requestauthor') {
return $env{'environment.canrequest.author'};
+ } elsif ($context eq 'authordefaults') {
+ if ($tool eq 'webdav') {
+ return $env{'environment.availabletools.'.$tool};
+ }
} else {
return $env{'environment.availabletools.'.$tool};
}
@@ -8004,7 +8402,11 @@ sub usertools_access {
my ($toolstatus,$inststatus,$envkey);
if ($context eq 'requestauthor') {
- $envkey = $context;
+ $envkey = $context;
+ } elsif ($context eq 'authordefaults') {
+ if ($tool eq 'webdav') {
+ $envkey = 'tools.'.$tool;
+ }
} else {
$envkey = $context.'.'.$tool;
}
@@ -8116,7 +8518,8 @@ sub is_course_owner {
}
sub is_advanced_user {
- my ($udom,$uname) = @_;
+ my ($udom,$uname,$nocache) = @_;
+ my ($is_adv,$is_author,$use_cache,$hashid);
if ($udom ne '' && $uname ne '') {
if (($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) {
if (wantarray) {
@@ -8124,11 +8527,21 @@ sub is_advanced_user {
} else {
return $env{'user.adv'};
}
+ } elsif (!$nocache) {
+ $use_cache = 1;
+ $hashid = "$udom:$uname";
+ my ($info,$cached)=&is_cached_new('isadvau',$hashid);
+ if ($cached) {
+ ($is_adv,$is_author) = split(/:/,$info);
+ if (wantarray) {
+ return ($is_adv,$is_author);
+ }
+ return $is_adv;
+ }
}
}
my %roleshash = &get_my_roles($uname,$udom,'userroles',undef,undef,undef,1);
my %allroles;
- my ($is_adv,$is_author);
foreach my $role (keys(%roleshash)) {
my ($trest,$tdomain,$trole,$sec) = split(/:/,$role);
my $area = '/'.$tdomain.'/'.$trest;
@@ -8159,6 +8572,10 @@ sub is_advanced_user {
}
}
}
+ if ($use_cache) {
+ my $cachetime = 600;
+ &do_cache_new('isadvau',$hashid,$is_adv.':'.$is_author,$cachetime);
+ }
if (wantarray) {
return ($is_adv,$is_author);
}
@@ -8563,7 +8980,7 @@ sub allowed {
# If this is generating or modifying users, exit with special codes
- if (':csu:cdc:ccc:cin:cta:cep:ccr:cst:cad:cli:cau:cdg:cca:caa:'=~/\:\Q$priv\E\:/) {
+ if (':csu:cdc:ccc:cin:cta:cep:ccr:cst:cad:cli:cau:cdg:cca:caa:vca:vaa:'=~/\:\Q$priv\E\:/) {
if (($priv eq 'cca') || ($priv eq 'caa')) {
my ($audom,$auname)=split('/',$uri);
# no author name given, so this just checks on the general right to make a co-author in this domain
@@ -8572,6 +8989,13 @@ sub allowed {
if (($auname ne $env{'user.name'} && $env{'request.role'} !~ /^dc\./) ||
(($audom ne $env{'user.domain'} && $env{'request.role'} !~ /^dc\./) &&
($audom ne $env{'request.role.domain'}))) { return ''; }
+ } elsif (($priv eq 'vca') || ($priv eq 'vaa')) {
+ my ($audom,$auname)=split('/',$uri);
+ unless ($auname) { return $thisallowed; }
+ unless (($env{'request.role'} eq "dc./$audom") ||
+ ($env{'request.role'} eq "ca./$uri")) {
+ return '';
+ }
}
return $thisallowed;
}
@@ -8583,6 +9007,12 @@ sub allowed {
if ($env{'request.course.id'}) {
+ if ($priv eq 'bre') {
+ if (&is_coursetool_logo($uri)) {
+ return 'F';
+ }
+ }
+
# If this is modifying password (internal auth) domains must match for user and user's role.
if ($priv eq 'mip') {
@@ -8903,22 +9333,41 @@ sub constructaccess {
if (exists($env{'user.priv.au./'.$ownerdomain.'/./'})) {
return ($ownername,$ownerdomain,$ownerhome);
}
- } else {
-# Co-author for this?
- if (exists($env{'user.priv.ca./'.$ownerdomain.'/'.$ownername.'./'}) ||
- exists($env{'user.priv.aa./'.$ownerdomain.'/'.$ownername.'./'}) ) {
- $ownerhome = &homeserver($ownername,$ownerdomain);
- return ($ownername,$ownerdomain,$ownerhome);
- }
+ } elsif (&is_course($ownerdomain,$ownername)) {
+# Course Authoring Space?
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'})) {
+ return if ($env{'course.'.$env{'request.course.id'}.'.internal.crsauthor'} eq '0');
+ unless ($env{'course.'.$env{'request.course.id'}.'.internal.crsauthor'}) {
+ my %domdefs = &get_domain_defaults($ownerdomain);
+ my $type = lc($env{'course.'.$env{'request.course.id'}.'.type'});
+ unless (($type eq 'community') || ($type eq 'placement')) {
+ $type = 'unofficial';
+ if ($env{'course.'.$env{'request.course.id'}.'internal.coursecode'} ne '') {
+ $type = 'official';
+ } elsif ($env{'course.'.$env{'request.course.id'}.'internal.textbook'} ne '') {
+ $type = 'textbook';
+ } else {
+ $type = 'unofficial';
+ }
+ }
+ return if ($domdefs{$type.'crsauthor'} eq '0');
+ }
$ownerhome = $env{'course.'.$env{'request.course.id'}.'.home'};
return ($ownername,$ownerdomain,$ownerhome);
}
}
}
+ return '';
+ } else {
+# Co-author for this?
+ if (exists($env{'user.priv.ca./'.$ownerdomain.'/'.$ownername.'./'}) ||
+ exists($env{'user.priv.aa./'.$ownerdomain.'/'.$ownername.'./'}) ) {
+ $ownerhome = &homeserver($ownername,$ownerdomain);
+ return ($ownername,$ownerdomain,$ownerhome);
+ }
}
# We don't have any access right now. If we are not possibly going to do anything about this,
@@ -9985,7 +10434,7 @@ sub auto_instsec_reformat {
my $info = &freeze_escape($instsecref);
my $response=&reply('autoinstsecreformat:'.$cdom.':'.
$action.':'.$info,$server);
- next if ($response =~ /(con_lost|error|no_such_host|refused|unknown_command)/);
+ next if ($response =~ /(con_lost|error|no_such_host|refused|unknown_cmd)/);
my @items = split(/&/,$response);
foreach my $item (@items) {
my ($key,$value) = split(/=/,$item);
@@ -10067,7 +10516,7 @@ sub auto_export_grades {
my $grades = &freeze_escape($gradesref);
my $response=&reply('encrypt:autoexportgrades:'.$cdom.':'.$cnum.':'.
$info.':'.$grades,$homeserver);
- unless ($response =~ /(con_lost|error|no_such_host|refused|unknown_command)/) {
+ unless ($response =~ /(con_lost|error|no_such_host|refused|unknown_cmd)/) {
my @items = split(/&/,$response);
foreach my $item (@items) {
my ($key,$value) = split('=',$item);
@@ -10197,11 +10646,13 @@ sub toggle_coursegroup_status {
}
sub modify_group_roles {
- my ($cdom,$cnum,$group_id,$user,$end,$start,$userprivs,$selfenroll,$context) = @_;
+ my ($cdom,$cnum,$group_id,$user,$end,$start,$userprivs,$selfenroll,$context,
+ $othdomby,$requester) = @_;
my $url = '/'.$cdom.'/'.$cnum.'/'.$group_id;
my $role = 'gr/'.&escape($userprivs);
my ($uname,$udom) = split(/:/,$user);
- my $result = &assignrole($udom,$uname,$url,$role,$end,$start,'',$selfenroll,$context);
+ my $result = &assignrole($udom,$uname,$url,$role,$end,$start,'',$selfenroll,$context,
+ $othdomby,$requester);
if ($result eq 'ok') {
&devalidate_getgroups_cache($udom,$uname,$cdom,$cnum);
}
@@ -10329,43 +10780,66 @@ sub plaintext {
sub assignrole {
my ($udom,$uname,$url,$role,$end,$start,$deleteflag,$selfenroll,
- $context)=@_;
- my $mrole;
+ $context,$othdomby,$requester,$reqsec,$reqrole)=@_;
+ my ($mrole,$rolelogcontext);
if ($role =~ /^cr\//) {
my $cwosec=$url;
$cwosec=~s/^\/($match_domain)\/($match_courseid)\/.*/$1\/$2/;
if ((!&allowed('ccr',$cwosec)) && (!&allowed('ccr',$udom))) {
- my $refused = 1;
- if ($context eq 'requestcourses') {
- if (($env{'user.name'} ne '') && ($env{'user.domain'} ne '')) {
- if ($role =~ m{^cr/($match_domain)/($match_username)/([^/]+)$}) {
- if (($1 eq $env{'user.domain'}) && ($2 eq $env{'user.name'})) {
- my ($cdom,$cnum) = ($cwosec =~ m{^/?($match_domain)/($match_courseid)$});
- my %crsenv = &userenvironment($cdom,$cnum,('internal.courseowner'));
- if ($crsenv{'internal.courseowner'} eq
- $env{'user.name'}.':'.$env{'user.domain'}) {
- $refused = '';
- }
- }
- }
- }
- }
- if ($refused) {
- &logthis('Refused custom assignrole: '.
- $udom.' '.$uname.' '.$url.' '.$role.' '.$end.' '.$start.
- ' by '.$env{'user.name'}.' at '.$env{'user.domain'});
- return 'refused';
- }
+ my $refused = 1;
+ if ($context eq 'requestcourses') {
+ if (($env{'user.name'} ne '') && ($env{'user.domain'} ne '')) {
+ if ($role =~ m{^cr/($match_domain)/($match_username)/([^/]+)$}) {
+ if (($1 eq $env{'user.domain'}) && ($2 eq $env{'user.name'})) {
+ my ($cdom,$cnum) = ($cwosec =~ m{^/?($match_domain)/($match_courseid)$});
+ my %crsenv = &userenvironment($cdom,$cnum,('internal.courseowner'));
+ if ($crsenv{'internal.courseowner'} eq
+ $env{'user.name'}.':'.$env{'user.domain'}) {
+ $refused = '';
+ }
+ }
+ }
+ }
+ } elsif (($context eq 'course') && ($othdomby eq 'othdombyuser')) {
+ my ($cdom,$cnum) = ($cwosec =~ m{^/?($match_domain)/($match_courseid)$});
+ my ($sec) = ($url =~ m{^/\Q$cwosec\E/(.*)$});
+ my $key = "$uname:$udom:$role:$sec";
+ my %queuedrolereq = &Apache::lonnet::get('nohist_othdomqueued',[$key],$cdom,$cnum);
+ if ((exists($queuedrolereq{$key})) && (ref($queuedrolereq{$key}) eq 'HASH')) {
+ if (($queuedrolereq{$key}{'adj'} eq 'user') && ($queuedrolereq{$key}{'requester'} eq $requester)) {
+ $refused = '';
+ }
+ }
+ }
+ if ($refused) {
+ &logthis('Refused custom assignrole: '.
+ $udom.' '.$uname.' '.$url.' '.$role.' '.$end.' '.$start.
+ ' by '.$env{'user.name'}.' at '.$env{'user.domain'});
+ return 'refused';
+ }
}
$mrole='cr';
} elsif ($role =~ /^gr\//) {
my $cwogrp=$url;
$cwogrp=~s{^/($match_domain)/($match_courseid)/.*}{$1/$2};
- unless (&allowed('mdg',$cwogrp)) {
- &logthis('Refused group assignrole: '.
- $udom.' '.$uname.' '.$url.' '.$role.' '.$end.' '.$start.' by '.
- $env{'user.name'}.' at '.$env{'user.domain'});
- return 'refused';
+ if (!&allowed('mdg',$cwogrp)) {
+ my $refused = 1;
+ if (($refused) && ($othdomby eq 'othdombyuser') && ($requester ne '') && ($reqrole ne '')) {
+ my ($cdom,$cnum) = ($cwogrp =~ m{^/?($match_domain)/($match_courseid)$});
+ my $key = "$uname:$udom:$reqrole:$reqsec";
+ my %queuedrolereq = &Apache::lonnet::get('nohist_othdomqueued',[$key],$cdom,$cnum);
+ if ((exists($queuedrolereq{$key})) && (ref($queuedrolereq{$key}) eq 'HASH')) {
+ if (($queuedrolereq{$key}{'adj'} eq 'user') && ($queuedrolereq{$key}{'requester'} eq $requester)) {
+ $refused = '';
+ }
+ }
+ }
+ if ($refused) {
+ &logthis('Refused group assignrole: '.
+ $udom.' '.$uname.' '.$url.' '.$role.' '.$end.' '.$start.' by '.
+ $env{'user.name'}.' at '.$env{'user.domain'});
+ return 'refused';
+ }
}
$mrole='gr';
} else {
@@ -10382,7 +10856,8 @@ sub assignrole {
}
if ($refused) {
my ($cdom,$cnum) = ($cwosec =~ m{^/?($match_domain)/($match_courseid)$});
- if (!$selfenroll && (($context eq 'course') || ($context eq 'ltienroll' && $env{'request.lti.login'}))) {
+ if (!$selfenroll && ($othdomby ne 'othdombyuser') &&
+ (($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'));
@@ -10408,6 +10883,49 @@ sub assignrole {
} elsif (($context eq 'ltienroll') && ($env{'request.lti.login'})) {
$refused = '';
}
+ } elsif ($othdomby eq 'othdombyuser') {
+ my ($key,%queuedrolereq);
+ if ($context eq 'course') {
+ my ($sec) = ($url =~ m{^/\Q$cwosec\E/(.*)$});
+ $key = "$uname:$udom:$role:$sec";
+ %queuedrolereq = &Apache::lonnet::get('nohist_othdomqueued',[$key],$cdom,$cnum);
+ if ((exists($queuedrolereq{$key})) && (ref($queuedrolereq{$key}) eq 'HASH')) {
+ if (($queuedrolereq{$key}{'adj'} eq 'user') && ($queuedrolereq{$key}{'requester'} eq $requester)) {
+ if ((($role eq 'cc') && ($cnum !~ /^$match_community$/)) ||
+ (($role eq 'co') && ($cnum =~ /^$match_community$/))) {
+ my %crsenv = &userenvironment($cdom,$cnum,('internal.courseowner'));
+ if ($crsenv{'internal.courseowner'} eq $requester) {
+ $refused = '';
+ }
+ } elsif ($role =~ /^(?:in|ta|ep|st)$/) {
+ $refused = '';
+ }
+ }
+ }
+ } elsif (($context eq 'author') && ($role =~ /^ca|aa$/)) {
+ my $key = "$uname:$udom:$role";
+ my ($audom,$auname) = ($url =~ m{^/($match_domain)/($match_username)$});
+ if (($audom ne '') && ($auname ne '')) {
+ my %queuedrolereq = &Apache::lonnet::get('nohist_othdomqueued',[$key],$audom,$auname);
+ if ((exists($queuedrolereq{$key})) && (ref($queuedrolereq{$key}) eq 'HASH')) {
+ if (($queuedrolereq{$key}{'adj'} eq 'user') && ($queuedrolereq{$key}{'requester'} eq $requester)) {
+ $refused = '';
+ }
+ }
+ }
+ } elsif (($context eq 'domain') && ($role ne 'dc') && ($role ne 'su')) {
+ my $key = "$uname:$udom:$role";
+ my ($roledom) = ($url =~ m{^/($match_domain)/\Q$role\E$});
+ if ($roledom ne '') {
+ my $confname = $roledom.'-domainconfig';
+ my %queuedrolereq = &Apache::lonnet::get('nohist_othdomqueued',[$key],$roledom,$confname);
+ if ((exists($queuedrolereq{$key})) && (ref($queuedrolereq{$key}) eq 'HASH')) {
+ if (($queuedrolereq{$key}{'adj'} eq 'user') && ($queuedrolereq{$key}{'requester'} eq $requester)) {
+ $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 '')) {
@@ -10460,6 +10978,15 @@ sub assignrole {
}
}
}
+ } elsif (($context eq 'author') && (($role eq 'ca' || $role eq 'aa'))) {
+ if ($url =~ m{^/($match_domain)/($match_username)$}) {
+ my ($audom,$auname) = ($1,$2);
+ if ((&Apache::lonnet::allowed('v'.$role,"$audom/$auname")) &&
+ ($env{"environment.internal.manager.$url"})) {
+ $refused = '';
+ $rolelogcontext = 'coauthor';
+ }
+ }
}
if ($refused) {
&logthis('Refused assignrole: '.$udom.' '.$uname.' '.$url.
@@ -10520,15 +11047,18 @@ sub assignrole {
$origstart,$selfenroll,$context);
}
&courserolelog($role,$uname,$udom,$url,$origstart,$origend,$delflag,
- $selfenroll,$context);
+ $selfenroll,$context,$othdomby,$requester);
} elsif (($role eq 'li') || ($role eq 'dg') || ($role eq 'sc') ||
($role eq 'au') || ($role eq 'dc') || ($role eq 'dh') ||
($role eq 'da')) {
&domainrolelog($role,$uname,$udom,$url,$origstart,$origend,$delflag,
- $context);
+ $context,$othdomby,$requester);
} elsif (($role eq 'ca') || ($role eq 'aa')) {
+ if ($rolelogcontext eq '') {
+ $rolelogcontext = $context;
+ }
&coauthorrolelog($role,$uname,$udom,$url,$origstart,$origend,$delflag,
- $context);
+ $rolelogcontext,$othdomby,$requester);
}
if ($role eq 'cc') {
&autoupdate_coowners($url,$end,$start,$uname,$udom);
@@ -10880,7 +11410,7 @@ sub modifystudent {
sub modify_student_enrollment {
my ($udom,$uname,$uid,$first,$middle,$last,$gene,$usec,$end,$start,$type,
- $locktype,$cid,$selfenroll,$context,$credits,$instsec) = @_;
+ $locktype,$cid,$selfenroll,$context,$credits,$instsec,$othdomby,$requester) = @_;
my ($cdom,$cnum,$chome);
if (!$cid) {
unless ($cid=$env{'request.course.id'}) {
@@ -10941,7 +11471,7 @@ sub modify_student_enrollment {
$uurl.='/'.$usec;
}
my $result = &assignrole($udom,$uname,$uurl,'st',$end,$start,undef,
- $selfenroll,$context);
+ $selfenroll,$context,$othdomby,$requester);
if ($result ne 'ok') {
if ($old_entry{$user} ne '') {
$reply = &cput('classlist',\%old_entry,$cdom,$cnum);
@@ -11179,7 +11709,7 @@ sub is_course {
}
sub store_userdata {
- my ($storehash,$datakey,$namespace,$udom,$uname) = @_;
+ my ($storehash,$datakey,$namespace,$udom,$uname,$ip) = @_;
my $result;
if ($datakey ne '') {
if (ref($storehash) eq 'HASH') {
@@ -11191,7 +11721,11 @@ sub store_userdata {
if (($uhome eq '') || ($uhome eq 'no_host')) {
$result = 'error: no_host';
} else {
- $storehash->{'ip'} = &get_requestor_ip();
+ if ($ip ne '') {
+ $storehash->{'ip'} = $ip;
+ } else {
+ $storehash->{'ip'} = &get_requestor_ip();
+ }
$storehash->{'host'} = $perlvar{'lonHostID'};
my $namevalue='';
@@ -11217,9 +11751,11 @@ sub store_userdata {
# ---------------------------------------------------------- Assign Custom Role
sub assigncustomrole {
- my ($udom,$uname,$url,$rdom,$rnam,$rolename,$end,$start,$deleteflag,$selfenroll,$context)=@_;
+ my ($udom,$uname,$url,$rdom,$rnam,$rolename,$end,$start,$deleteflag,
+ $selfenroll,$context,$othdomby,$requester)=@_;
return &assignrole($udom,$uname,$url,'cr/'.$rdom.'/'.$rnam.'/'.$rolename,
- $end,$start,$deleteflag,$selfenroll,$context);
+ $end,$start,$deleteflag,$selfenroll,$context,$othdomby,
+ $requester);
}
# ----------------------------------------------------------------- Revoke Role
@@ -12026,18 +12562,26 @@ sub stat_file {
# or corresponding Published Resource Space, and populate the hash ref:
# $dirhashref with URLs of all directories, and if $filehashref hash
# ref arg is provided, the URLs of any files, excluding versioned, .meta,
-# or .rights files in resource space, and .meta, .save, .log, and .bak
-# files in Authoring Space.
+# or .rights files in resource space, and .meta, .save, .log, .bak and
+# .rights files in Authoring Space.
#
# Inputs:
#
# $is_home - true if current server is home server for user's space
-# $context - either: priv, or res respectively for Authoring or Resource Space.
-# $docroot - Document root (i.e., /home/httpd/html
+# $recurse - if true will also traverse subdirectories recursively
+# $include - reference to hash containing allowed file extensions. If provided,
+# files which do not have a matching extension will be ignored.
+# $exclude - reference to hash containing excluded file extensions. If provided,
+# files which have a matching extension will be ignored.
+# $nonemptydir - if true, will only populate $fileshashref hash entry for a particular
+# directory with first file found (with acceptable extension).
+# $addtopdir - if true, set $dirhashref->{'/'} = 1
# $toppath - Top level directory (i.e., /res/$dom/$uname or /priv/$dom/$uname
# $relpath - Current path (relative to top level).
# $dirhashref - reference to hash to populate with URLs of directories (Required)
# $filehashref - reference to hash to populate with URLs of files (Optional)
+# $getlastmod - if true, will set value for each key in innerhash in $filehashref
+# to last modification time of file; value set to 1 otherwise.
#
# Returns: nothing
#
@@ -12050,49 +12594,83 @@ sub stat_file {
#
sub recursedirs {
- my ($is_home,$context,$docroot,$toppath,$relpath,$dirhashref,$filehashref) = @_;
+ my ($is_home,$recurse,$include,$exclude,$nonemptydir,$addtopdir,$toppath,
+ $relpath,$dirhashref,$filehashref,$getlastmod) = @_;
return unless (ref($dirhashref) eq 'HASH');
+ my $docroot = $perlvar{'lonDocRoot'};
my $currpath = $docroot.$toppath;
- if ($relpath) {
+ if ($relpath ne '') {
$currpath .= "/$relpath";
}
- my $savefile;
- if (ref($filehashref)) {
+ my ($savefile,$checkinc,$checkexc);
+ if (ref($filehashref) eq 'HASH') {
$savefile = 1;
}
+ if (ref($include) eq 'HASH') {
+ $checkinc = 1;
+ }
+ if (ref($exclude) eq 'HASH') {
+ $checkexc = 1;
+ }
if ($is_home) {
- if (opendir(my $dirh,$currpath)) {
+ if ((-e $currpath) && (opendir(my $dirh,$currpath))) {
+ my $filecount = 0;
foreach my $item (sort { lc($a) cmp lc($b) } grep(!/^\.+$/,readdir($dirh))) {
next if ($item eq '');
if (-d "$currpath/$item") {
my $newpath;
- if ($relpath) {
+ if ($relpath ne '') {
$newpath = "$relpath/$item";
} else {
$newpath = $item;
}
$dirhashref->{&Apache::lonlocal::js_escape($newpath)} = 1;
- &recursedirs($is_home,$context,$docroot,$toppath,$newpath,$dirhashref,$filehashref);
- } elsif ($savefile) {
- if ($context eq 'priv') {
- unless ($item =~ /\.(meta|save|log|bak|DS_Store)$/) {
- $filehashref->{&Apache::lonlocal::js_escape($relpath)}{$item} = 1;
+ if ($recurse) {
+ &recursedirs($is_home,$recurse,$include,$exclude,$nonemptydir,$addtopdir,
+ $toppath,$newpath,$dirhashref,$filehashref,$getlastmod);
+ }
+ } elsif (($savefile) || ($relpath eq '')) {
+ next if ($nonemptydir && $filecount);
+ if ($checkinc || $checkexc) {
+ my ($extension) = ($item =~ /\.(\w+)$/);
+ if ($checkinc) {
+ next unless ($extension && $include->{$extension});
+ }
+ if ($checkexc) {
+ next if ($extension && $exclude->{$extension});
+ }
+ }
+ if (($relpath eq '') && (!exists($dirhashref->{'/'}))) {
+ $dirhashref->{'/'} = 1;
+ }
+ if ($savefile) {
+ my $value;
+ if ($getlastmod) {
+ ($value) = (stat("$currpath/$item"))[9];
+ } else {
+ $value = 1;
}
- } else {
- unless (($item =~ /\.meta$/) || ($item =~ /\.\d+\.\w+$/) || ($item =~ /\.rights$/)) {
- $filehashref->{&Apache::lonlocal::js_escape($relpath)}{$item} = 1;
+ if ($relpath eq '') {
+ $filehashref->{'/'}{$item} = $value
+ } else {
+ $filehashref->{&Apache::lonlocal::js_escape($relpath)}{$item} = $value;
}
}
+ $filecount ++;
}
}
closedir($dirh);
}
} else {
- my ($dirlistref,$listerror) =
- &dirlist($toppath.$relpath);
+ my $url = $toppath;
+ if ($relpath ne '') {
+ $url = $toppath.'/'.$relpath;
+ }
+ my ($dirlistref,$listerror) = &dirlist($url);
my @dir_lines;
my $dirptr=16384;
if (ref($dirlistref) eq 'ARRAY') {
+ my $filecount = 0;
foreach my $dir_line (sort
{
my ($afile)=split('&',$a,2);
@@ -12108,28 +12686,72 @@ sub recursedirs {
if ($relpath) {
$newpath = "$relpath/$item";
} else {
- $relpath = '/';
$newpath = $item;
}
$dirhashref->{&Apache::lonlocal::js_escape($newpath)} = 1;
- &recursedirs($is_home,$context,$docroot,$toppath,$newpath,$dirhashref,$filehashref);
- } elsif ($savefile) {
- if ($context eq 'priv') {
- unless ($item =~ /\.(meta|save|log|bak|DS_Store)$/) {
- $filehashref->{$relpath}{$item} = 1;
+ if ($recurse) {
+ &recursedirs($is_home,$recurse,$include,$exclude,$nonemptydir,$addtopdir,
+ $toppath,$newpath,$dirhashref,$filehashref,$getlastmod);
+ }
+ } elsif (($savefile) || ($relpath eq '')) {
+ next if ($nonemptydir && $filecount);
+ if ($checkinc || $checkexc) {
+ my ($extension) = ($item =~ /\.(\w+)$/);
+ if ($checkinc) {
+ next unless ($extension && $include->{$extension});
+ }
+ if ($checkexc) {
+ next if ($extension && $exclude->{$extension});
+ }
+ }
+ if (($relpath eq '') && (!exists($dirhashref->{'/'}))) {
+ $dirhashref->{'/'} = 1;
+ }
+ if ($savefile) {
+ my $value;
+ if ($getlastmod) {
+ $value = $mtime;
+ } else {
+ $value = 1;
}
- } else {
- unless (($item =~ /\.meta$/) || ($item =~ /\.\d+\.\w+$/)) {
- $filehashref->{$relpath}{$item} = 1;
+ if ($relpath eq '') {
+ $filehashref->{'/'}{$item} = $value;
+ } else {
+ $filehashref->{&Apache::lonlocal::js_escape($relpath)}{$item} = $value;
}
}
+ $filecount ++;
}
}
}
}
+ if ($addtopdir) {
+ if (($relpath eq '') && (!exists($dirhashref->{'/'}))) {
+ $dirhashref->{'/'} = 1;
+ }
+ }
return;
}
+sub priv_exclude {
+ return {
+ meta => 1,
+ save => 1,
+ log => 1,
+ bak => 1,
+ rights => 1,
+ DS_Store => 1,
+ };
+}
+
+sub res_exclude {
+ return {
+ meta => 1,
+ subscription => 1,
+ rights => 1,
+ };
+}
+
# -------------------------------------------------------- Value of a Condition
# gets the value of a specific preevaluated condition
@@ -12353,13 +12975,11 @@ sub get_domain_lti {
} else {
return %lti;
}
-
if ($context eq 'linkprot') {
$cachename = $context;
} else {
$cachename = $name;
}
-
my ($result,$cached)=&is_cached_new($cachename,$cdom);
if (defined($cached)) {
if (ref($result) eq 'HASH') {
@@ -12375,18 +12995,6 @@ sub get_domain_lti {
} else {
%lti = %{$domconfig{$name}};
}
- if (($context eq 'consumer') && (keys(%lti))) {
- my %encdomconfig = &get_dom('encconfig',[$name],$cdom,undef,1);
- if (ref($encdomconfig{$name}) eq 'HASH') {
- foreach my $id (keys(%lti)) {
- if (ref($encdomconfig{$name}{$id}) eq 'HASH') {
- foreach my $item ('key','secret') {
- $lti{$id}{$item} = $encdomconfig{$name}{$id}{$item};
- }
- }
- }
- }
- }
}
my $cachetime = 24*60*60;
&do_cache_new($cachename,$cdom,\%lti,$cachetime);
@@ -12395,20 +13003,29 @@ sub get_domain_lti {
}
sub get_course_lti {
- my ($cnum,$cdom) = @_;
+ my ($cnum,$cdom,$context) = @_;
+ my ($name,$cachename,%lti);
+ if ($context eq 'consumer') {
+ $name = 'ltitools';
+ $cachename = 'courseltitools';
+ } elsif ($context eq 'provider') {
+ $name = 'lti';
+ $cachename = 'courselti';
+ } else {
+ return %lti;
+ }
my $hashid=$cdom.'_'.$cnum;
- my %courselti;
- my ($result,$cached)=&is_cached_new('courselti',$hashid);
+ my ($result,$cached)=&is_cached_new($cachename,$hashid);
if (defined($cached)) {
if (ref($result) eq 'HASH') {
- %courselti = %{$result};
+ %lti = %{$result};
}
} else {
- %courselti = &dump('lti',$cdom,$cnum,undef,undef,undef,1);
+ %lti = &dump($name,$cdom,$cnum,undef,undef,undef,1);
my $cachetime = 24*60*60;
- &do_cache_new('courselti',$hashid,\%courselti,$cachetime);
+ &do_cache_new($cachename,$hashid,\%lti,$cachetime);
}
- return %courselti;
+ return %lti;
}
sub courselti_itemid {
@@ -12458,6 +13075,73 @@ sub domainlti_itemid {
return $itemid;
}
+sub get_ltitools_id {
+ my ($context,$cdom,$cnum,$title) = @_;
+ my ($lockhash,$tries,$gotlock,$id,$error);
+
+ # get lock on ltitools db
+ $lockhash = {
+ lock => $env{'user.name'}.
+ ':'.$env{'user.domain'},
+ };
+ $tries = 0;
+ if ($context eq 'domain') {
+ $gotlock = &newput_dom('ltitools',$lockhash,$cdom);
+ } else {
+ $gotlock = &newput('ltitools',$lockhash,$cdom,$cnum);
+ }
+ while (($gotlock ne 'ok') && ($tries<10)) {
+ $tries ++;
+ sleep (0.1);
+ if ($context eq 'domain') {
+ $gotlock = &newput_dom('ltitools',$lockhash,$cdom);
+ } else {
+ $gotlock = &newput('ltitools',$lockhash,$cdom,$cnum);
+ }
+ }
+ if ($gotlock eq 'ok') {
+ my %currids;
+ if ($context eq 'domain') {
+ %currids = &dump_dom('ltitools',$cdom);
+ } else {
+ %currids = &dump('ltitools',$cdom,$cnum);
+ }
+ if ($currids{'lock'}) {
+ delete($currids{'lock'});
+ if (keys(%currids)) {
+ my @curr = sort { $a <=> $b } keys(%currids);
+ if ($curr[-1] =~ /^\d+$/) {
+ $id = 1 + $curr[-1];
+ }
+ } else {
+ $id = 1;
+ }
+ if ($id) {
+ if ($context eq 'domain') {
+ unless (&newput_dom('ltitools',{ $id => $title },$cdom) eq 'ok') {
+ $error = 'nostore';
+ }
+ } else {
+ unless (&newput('ltitools',{ $id => $title },$cdom,$cnum) eq 'ok') {
+ $error = 'nostore';
+ }
+ }
+ } else {
+ $error = 'nonumber';
+ }
+ }
+ my $dellockoutcome;
+ if ($context eq 'domain') {
+ $dellockoutcome = &del_dom('ltitools',['lock'],$cdom);
+ } else {
+ $dellockoutcome = &del('ltitools',['lock'],$cdom,$cnum);
+ }
+ } else {
+ $error = 'nolock';
+ }
+ return ($id,$error);
+}
+
sub count_supptools {
my ($cnum,$cdom,$ignorecache,$reload)=@_;
my $hashid=$cnum.':'.$cdom;
@@ -14433,6 +15117,9 @@ sub whichuser {
$courseid=$tmp_courseid;
($domain)=&get_env_multiple('form.grade_domain');
($name)=&get_env_multiple('form.grade_username');
+ if ($name eq 'public' && $domain eq 'public') {
+ $publicuser = 1;
+ }
return ($symb,$courseid,$domain,$name,$publicuser);
}
}
@@ -14449,6 +15136,7 @@ sub whichuser {
$env{'form.username'}.=time.rand(10000000);
}
$name.=$env{'form.username'};
+ $publicuser = 1;
}
return ($symb,$courseid,$domain,$name,$publicuser);
@@ -14537,6 +15225,49 @@ sub repcopy_userfile {
return 'ok';
}
+sub repcopy_crsprivfile {
+ my ($src,$dest) = @_;
+ my $result;
+ if ($src =~ m{^/priv/($match_domain)/($match_courseid)/(.+)$}) {
+ my ($cdom,$cnum,$filepath) = ($1,$2,$3);
+ $filepath =~ s/\.{2,}//g;
+ my $chome = &homeserver($cnum,$cdom);
+ unless ($chome eq 'no_host') {
+ my @ids=¤t_machine_ids();
+ unless (grep(/^\Q$chome\E$/,@ids)) {
+ if (&is_course($cdom,$cnum)) {
+ my $londocroot = $perlvar{'lonDocRoot'};
+ if ($dest =~ m{^\Q$londocroot/priv/\E$match_domain/$match_username/.*\Q$filepath\E$}) {
+ my $cmd = 'crsfilefrompriv:'.&escape($filepath).':'.&escape($cnum).':'.&escape($cdom);
+ $result = &reply($cmd,$chome);
+ unless (($result eq 'unknown_cmd') || ($result =~ /^error:/)) {
+ my $url = &unescape($result);
+ if ($url =~ m{^https?://[^/]+\Q/userfiles/$cdom/$cnum/priv/$filepath\E$}) {
+ my $request=new HTTP::Request('GET',$url);
+ my $response=&LONCAPA::LWPReq::makerequest($chome,$request,'',\%perlvar,1200,1);
+ if ($response->is_error()) {
+ $result = 'error: '.$response->status_line;
+ } else {
+ if (open(my $fh,'>',$dest)) {
+ print $fh $response->content;
+ close($fh);
+ $result = 'ok';
+ } else {
+ $result = 'error: nowrite';
+ }
+ }
+ } else {
+ $result = 'error: invalidurl';
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return $result;
+}
+
sub tokenwrapper {
my $uri=shift;
$uri=~s|^https?\://([^/]+)||;