--- loncom/lonnet/perl/lonnet.pm 2005/11/22 00:01:53 1.683
+++ loncom/lonnet/perl/lonnet.pm 2006/10/13 04:23:15 1.791
@@ -1,7 +1,7 @@
# The LearningOnline Network
# TCP networking package
#
-# $Id: lonnet.pm,v 1.683 2005/11/22 00:01:53 raeburn Exp $
+# $Id: lonnet.pm,v 1.791 2006/10/13 04:23:15 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -38,14 +38,13 @@ use vars
qw(%perlvar %hostname %badServerCache %iphost %spareid %hostdom
%libserv %pr %prp $memcache %packagetab
%courselogs %accesshash %userrolehash %domainrolehash $processmarker $dumpcount
- %coursedombuf %coursenumbuf %coursehombuf %coursedescrbuf %courseinstcodebuf %courseownerbuf
+ %coursedombuf %coursenumbuf %coursehombuf %coursedescrbuf %courseinstcodebuf %courseownerbuf %coursetypebuf
%domaindescription %domain_auth_def %domain_auth_arg_def
- %domain_lang_def %domain_city %domain_longi %domain_lati $tmpdir $_64bit
- %env);
+ %domain_lang_def %domain_city %domain_longi %domain_lati %domain_primary
+ $tmpdir $_64bit %env);
use IO::Socket;
use GDBM_File;
-use Apache::Constants qw(:common :http);
use HTML::LCParser;
use HTML::Parser;
use Fcntl qw(:flock);
@@ -53,6 +52,10 @@ use Storable qw(lock_store lock_nstore l
use Time::HiRes qw( gettimeofday tv_interval );
use Cache::Memcached;
use Digest::MD5;
+use Math::Random;
+use lib '/home/httpd/lib/perl';
+use LONCAPA;
+use LONCAPA::Configuration;
my $readit;
my $max_connection_retries = 10; # Or some such value.
@@ -86,6 +89,29 @@ delayed.
# --------------------------------------------------------------------- Logging
+{
+ my $logid;
+ sub instructor_log {
+ my ($hash_name,$storehash,$delflag,$uname,$udom)=@_;
+ $logid++;
+ my $id=time().'00000'.$$.'00000'.$logid;
+ return &Apache::lonnet::put('nohist_'.$hash_name,
+ { $id => {
+ 'exe_uname' => $env{'user.name'},
+ 'exe_udom' => $env{'user.domain'},
+ 'exe_time' => time(),
+ 'exe_ip' => $ENV{'REMOTE_ADDR'},
+ 'delflag' => $delflag,
+ 'logentry' => $storehash,
+ 'uname' => $uname,
+ 'udom' => $udom,
+ }
+ },
+ $env{'course.'.$env{'request.course.id'}.'.domain'},
+ $env{'course.'.$env{'request.course.id'}.'.num'}
+ );
+ }
+}
sub logtouch {
my $execdir=$perlvar{'lonDaemons'};
@@ -124,7 +150,7 @@ sub logperm {
# -------------------------------------------------- Non-critical communication
sub subreply {
my ($cmd,$server)=@_;
- my $peerfile="$perlvar{'lonSockDir'}/$server";
+ my $peerfile="$perlvar{'lonSockDir'}/".$hostname{$server};
#
# With loncnew process trimming, there's a timing hole between lonc server
# process exit and the master server picking up the listen on the AF_UNIX
@@ -152,7 +178,7 @@ sub subreply {
}
my $answer;
if ($client) {
- print $client "$cmd\n";
+ print $client "sethost:$server:$cmd\n";
$answer=<$client>;
if (!$answer) { $answer="con_lost"; }
chomp($answer);
@@ -256,9 +282,18 @@ sub critical {
return $answer;
}
-# ------------------------------------------- Transfer profile into environment
+# ------------------------------------------- check if return value is an error
-sub transfer_profile_to_env {
+sub error {
+ my ($result) = @_;
+ if ($result =~ /^(con_lost|no_such_host|error: (\d+) (.*))/) {
+ if ($2 == 2) { return undef; }
+ return $1;
+ }
+ return undef;
+}
+
+sub convert_and_load_session_env {
my ($lonidsdir,$handle)=@_;
my @profile;
{
@@ -267,20 +302,68 @@ sub transfer_profile_to_env {
@profile=<$idf>;
close($idf);
}
- my $envi;
- my %Remove;
- for ($envi=0;$envi<=$#profile;$envi++) {
- chomp($profile[$envi]);
- my ($envname,$envvalue)=split(/=/,$profile[$envi]);
- $env{$envname} = $envvalue;
+ my %temp_env;
+ foreach my $line (@profile) {
+ if ($line !~ m/=/) {
+ return 0;
+ }
+ chomp($line);
+ my ($envname,$envvalue)=split(/=/,$line,2);
+ $temp_env{&unescape($envname)} = &unescape($envvalue);
+ }
+ unlink("$lonidsdir/$handle.id");
+ if (tie(my %disk_env,'GDBM_File',"$lonidsdir/$handle.id",&GDBM_WRCREAT(),
+ 0640)) {
+ %disk_env = %temp_env;
+ @env{keys(%temp_env)} = @disk_env{keys(%temp_env)};
+ untie(%disk_env);
+ }
+ return 1;
+}
+
+# ------------------------------------------- Transfer profile into environment
+my $env_loaded;
+sub transfer_profile_to_env {
+ my ($lonidsdir,$handle,$force_transfer) = @_;
+ if (!$force_transfer && $env_loaded) { return; }
+
+ if (!defined($lonidsdir)) {
+ $lonidsdir = $perlvar{'lonIDsDir'};
+ }
+ if (!defined($handle)) {
+ ($handle) = ($env{'user.environment'} =~m|/([^/]+)\.id$| );
+ }
+
+ my $convert;
+ {
+ open(my $idf,"$lonidsdir/$handle.id");
+ flock($idf,LOCK_SH);
+ if (tie(my %disk_env,'GDBM_File',"$lonidsdir/$handle.id",
+ &GDBM_READER(),0640)) {
+ @env{keys(%disk_env)} = @disk_env{keys(%disk_env)};
+ untie(%disk_env);
+ } else {
+ $convert = 1;
+ }
+ }
+ if ($convert) {
+ if (!&convert_and_load_session_env($lonidsdir,$handle)) {
+ &logthis("Failed to load session, or convert session.");
+ }
+ }
+
+ my %remove;
+ while ( my $envname = each(%env) ) {
if (my ($key,$time) = ($envname =~ /^(cgi\.(\d+)_\d+\.)/)) {
if ($time < time-300) {
- $Remove{$key}++;
+ $remove{$key}++;
}
}
}
+
$env{'user.environment'} = "$lonidsdir/$handle.id";
- foreach my $expired_key (keys(%Remove)) {
+ $env_loaded=1;
+ foreach my $expired_key (keys(%remove)) {
&delenv($expired_key);
}
}
@@ -289,110 +372,72 @@ sub transfer_profile_to_env {
sub appenv {
my %newenv=@_;
- foreach (keys %newenv) {
- if (($newenv{$_}=~/^user\.role/) || ($newenv{$_}=~/^user\.priv/)) {
+ foreach my $key (keys(%newenv)) {
+ if (($newenv{$key}=~/^user\.role/) || ($newenv{$key}=~/^user\.priv/)) {
&logthis("WARNING: ".
- "Attempt to modify environment ".$_." to ".$newenv{$_}
+ "Attempt to modify environment ".$key." to ".$newenv{$key}
.'');
- delete($newenv{$_});
+ delete($newenv{$key});
} else {
- $env{$_}=$newenv{$_};
+ $env{$key}=$newenv{$key};
}
}
-
- my $lockfh;
- unless (open($lockfh,"$env{'user.environment'}")) {
- return 'error: '.$!;
- }
- unless (flock($lockfh,LOCK_EX)) {
- &logthis("WARNING: ".
- 'Could not obtain exclusive lock in appenv: '.$!);
- close($lockfh);
- return 'error: '.$!;
- }
-
- my @oldenv;
- {
- my $fh;
- unless (open($fh,"$env{'user.environment'}")) {
- return 'error: '.$!;
+ if (tie(my %disk_env,'GDBM_File',$env{'user.environment'},&GDBM_WRITER(),
+ 0640)) {
+ while (my ($key,$value) = each(%newenv)) {
+ $disk_env{$key} = $value;
}
- @oldenv=<$fh>;
- close($fh);
+ untie(%disk_env);
}
- for (my $i=0; $i<=$#oldenv; $i++) {
- chomp($oldenv[$i]);
- if ($oldenv[$i] ne '') {
- my ($name,$value)=split(/=/,$oldenv[$i]);
- unless (defined($newenv{$name})) {
- $newenv{$name}=$value;
- }
- }
- }
- {
- my $fh;
- unless (open($fh,">$env{'user.environment'}")) {
- return 'error';
- }
- my $newname;
- foreach $newname (keys %newenv) {
- print $fh "$newname=$newenv{$newname}\n";
- }
- close($fh);
- }
-
- close($lockfh);
return 'ok';
}
# ----------------------------------------------------- Delete from Environment
sub delenv {
my $delthis=shift;
- my %newenv=();
if (($delthis=~/user\.role/) || ($delthis=~/user\.priv/)) {
&logthis("WARNING: ".
"Attempt to delete from environment ".$delthis);
return 'error';
}
- my @oldenv;
- {
- my $fh;
- unless (open($fh,"$env{'user.environment'}")) {
- return 'error';
- }
- unless (flock($fh,LOCK_SH)) {
- &logthis("WARNING: ".
- 'Could not obtain shared lock in delenv: '.$!);
- close($fh);
- return 'error: '.$!;
- }
- @oldenv=<$fh>;
- close($fh);
- }
- {
- my $fh;
- unless (open($fh,">$env{'user.environment'}")) {
- return 'error';
- }
- unless (flock($fh,LOCK_EX)) {
- &logthis("WARNING: ".
- 'Could not obtain exclusive lock in delenv: '.$!);
- close($fh);
- return 'error: '.$!;
- }
- foreach (@oldenv) {
- if ($_=~/^$delthis/) {
- my ($key,undef) = split('=',$_);
+ if (tie(my %disk_env,'GDBM_File',$env{'user.environment'},&GDBM_WRITER(),
+ 0640)) {
+ foreach my $key (keys(%disk_env)) {
+ if ($key=~/^$delthis/) {
delete($env{$key});
- } else {
- print $fh $_;
+ delete($disk_env{$key});
}
}
- close($fh);
+ untie(%disk_env);
}
return 'ok';
}
+=pod
+
+=item * get_env_multiple($name)
+
+gets $name from the %env hash, it seemlessly handles the cases where multiple
+values may be defined and end up as an array ref.
+
+returns an array of values
+
+=cut
+
+sub get_env_multiple {
+ my ($name) = @_;
+ my @values;
+ if (defined($env{$name})) {
+ # exists is it an array
+ if (ref($env{$name})) {
+ @values=@{ $env{$name} };
+ } else {
+ $values[0]=$env{$name};
+ }
+ }
+ return(@values);
+}
+
# ------------------------------------------ Find out current server userload
# there is a copy in lond
sub userload {
@@ -445,41 +490,60 @@ sub overloaderror {
sub spareserver {
my ($loadpercent,$userloadpercent,$want_server_name) = @_;
- my $tryserver;
- my $spareserver='';
+ my $spare_server;
if ($userloadpercent !~ /\d/) { $userloadpercent=0; }
- my $lowestserver=$loadpercent > $userloadpercent?
- $loadpercent : $userloadpercent;
- foreach $tryserver (keys(%spareid)) {
- my $loadans=&reply('load',$tryserver);
- my $userloadans=&reply('userload',$tryserver);
- if ($loadans !~ /\d/ && $userloadans !~ /\d/) {
- next; #didn't get a number from the server
- }
- my $answer;
- if ($loadans =~ /\d/) {
- if ($userloadans =~ /\d/) {
- #both are numbers, pick the bigger one
- $answer=$loadans > $userloadans?
- $loadans : $userloadans;
- } else {
- $answer = $loadans;
- }
- } else {
- $answer = $userloadans;
- }
- if (($answer =~ /\d/) && ($answer<$lowestserver)) {
- if ($want_server_name) {
- $spareserver=$tryserver;
- } else {
- $spareserver="http://$hostname{$tryserver}";
- }
- $lowestserver=$answer;
+ my $lowest_load=($loadpercent > $userloadpercent) ? $loadpercent
+ : $userloadpercent;
+
+ foreach my $try_server (@{ $spareid{'primary'} }) {
+ ($spare_server, $lowest_load) =
+ &compare_server_load($try_server, $spare_server, $lowest_load);
+ }
+
+ my $found_server = ($spare_server ne '' && $lowest_load < 100);
+
+ if (!$found_server) {
+ foreach my $try_server (@{ $spareid{'default'} }) {
+ ($spare_server, $lowest_load) =
+ &compare_server_load($try_server, $spare_server, $lowest_load);
}
}
- return $spareserver;
+
+ if (!$want_server_name) {
+ $spare_server="http://$hostname{$spare_server}";
+ }
+ return $spare_server;
}
+sub compare_server_load {
+ my ($try_server, $spare_server, $lowest_load) = @_;
+
+ my $loadans = &reply('load', $try_server);
+ my $userloadans = &reply('userload',$try_server);
+
+ if ($loadans !~ /\d/ && $userloadans !~ /\d/) {
+ next; #didn't get a number from the server
+ }
+
+ my $load;
+ if ($loadans =~ /\d/) {
+ if ($userloadans =~ /\d/) {
+ #both are numbers, pick the bigger one
+ $load = ($loadans > $userloadans) ? $loadans
+ : $userloadans;
+ } else {
+ $load = $loadans;
+ }
+ } else {
+ $load = $userloadans;
+ }
+
+ if (($load =~ /\d/) && ($load < $lowest_load)) {
+ $spare_server = $try_server;
+ $lowest_load = $load;
+ }
+ return ($spare_server,$lowest_load);
+}
# --------------------------------------------- Try to change a user's password
sub changepass {
@@ -840,11 +904,10 @@ sub getsection {
}
sub save_cache {
- my ($r)=@_;
- if (! $r->is_initial_req()) { return DECLINED; }
&purge_remembered();
+ #&Apache::loncommon::validate_page();
undef(%env);
- return OK;
+ undef($env_loaded);
}
my $to_remember=-1;
@@ -947,25 +1010,62 @@ sub userenvironment {
sub studentphoto {
my ($udom,$unam,$ext) = @_;
my $home=&Apache::lonnet::homeserver($unam,$udom);
- my $ret=&Apache::lonnet::reply("studentphoto:$udom:$unam:$ext",$home);
- my $url="/uploaded/$udom/$unam/internal/studentphoto.".$ext;
- if ($ret ne 'ok') {
- return '/adm/lonKaputt/lonlogo_broken.gif';
+ if (defined($env{'request.course.id'})) {
+ if ($env{'course.'.$env{'request.course.id'}.'.internal.showphoto'}) {
+ if ($udom eq $env{'course.'.$env{'request.course.id'}.'.domain'}) {
+ return(&retrievestudentphoto($udom,$unam,$ext));
+ } else {
+ my ($result,$perm_reqd)=
+ &Apache::lonnet::auto_photo_permission($unam,$udom);
+ if ($result eq 'ok') {
+ if (!($perm_reqd eq 'yes')) {
+ return(&retrievestudentphoto($udom,$unam,$ext));
+ }
+ }
+ }
+ }
+ } else {
+ my ($result,$perm_reqd) =
+ &Apache::lonnet::auto_photo_permission($unam,$udom);
+ if ($result eq 'ok') {
+ if (!($perm_reqd eq 'yes')) {
+ return(&retrievestudentphoto($udom,$unam,$ext));
+ }
+ }
+ }
+ return '/adm/lonKaputt/lonlogo_broken.gif';
+}
+
+sub retrievestudentphoto {
+ my ($udom,$unam,$ext,$type) = @_;
+ my $home=&Apache::lonnet::homeserver($unam,$udom);
+ my $ret=&Apache::lonnet::reply("studentphoto:$udom:$unam:$ext:$type",$home);
+ if ($ret eq 'ok') {
+ my $url="/uploaded/$udom/$unam/internal/studentphoto.$ext";
+ if ($type eq 'thumbnail') {
+ $url="/uploaded/$udom/$unam/internal/studentphoto_tn.$ext";
+ }
+ my $tokenurl=&Apache::lonnet::tokenwrapper($url);
+ return $tokenurl;
+ } else {
+ if ($type eq 'thumbnail') {
+ return '/adm/lonKaputt/genericstudent_tn.gif';
+ } else {
+ return '/adm/lonKaputt/lonlogo_broken.gif';
+ }
}
- my $tokenurl=&Apache::lonnet::tokenwrapper($url);
- return $tokenurl;
}
# -------------------------------------------------------------------- New chat
sub chatsend {
- my ($newentry,$anon)=@_;
+ my ($newentry,$anon,$group)=@_;
my $cnum=$env{'course.'.$env{'request.course.id'}.'.num'};
my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
my $chome=$env{'course.'.$env{'request.course.id'}.'.home'};
&reply('chatsend:'.$cdom.':'.$cnum.':'.
&escape($env{'user.domain'}.':'.$env{'user.name'}.':'.$anon.':'.
- &escape($newentry)),$chome);
+ &escape($newentry)).':'.$group,$chome);
}
# ------------------------------------------ Find current version of a resource
@@ -1093,7 +1193,7 @@ sub ssi_body {
}
my $output=($filelink=~/^http\:/?&externalssi($filelink):
&ssi($filelink,%form));
- $output=~s|//(\s*)?\s||gs;
+ $output=~s|//(\s*)?\s||gs;
$output=~s/^.*?\]*\>//si;
$output=~s/(.*)\<\/body\s*\>.*?$/$1/si;
return $output;
@@ -1101,6 +1201,15 @@ sub ssi_body {
# --------------------------------------------------------- Server Side Include
+sub absolute_url {
+ my ($host_name) = @_;
+ my $protocol = ($ENV{'SERVER_PORT'} == 443?'https://':'http://');
+ if ($host_name eq '') {
+ $host_name = $ENV{'SERVER_NAME'};
+ }
+ return $protocol.$host_name;
+}
+
sub ssi {
my ($fn,%form)=@_;
@@ -1108,12 +1217,14 @@ sub ssi {
my $ua=new LWP::UserAgent;
my $request;
-
+
+ $form{'no_update_last_known'}=1;
+
if (%form) {
- $request=new HTTP::Request('POST',"http://".$ENV{'HTTP_HOST'}.$fn);
+ $request=new HTTP::Request('POST',&absolute_url().$fn);
$request->content(join('&',map { &escape($_).'='.&escape($form{$_}) } keys %form));
} else {
- $request=new HTTP::Request('GET',"http://".$ENV{'HTTP_HOST'}.$fn);
+ $request=new HTTP::Request('GET',&absolute_url().$fn);
}
$request->header(Cookie => $ENV{'HTTP_COOKIE'});
@@ -1280,12 +1391,19 @@ sub clean_filename {
}
# --------------- Take an uploaded file and put it into the userfiles directory
-# input: name of form element, coursedoc=1 means this is for the course
-# output: url of file in userspace
+# input: $formname - the contents of the file are in $env{"form.$formname"}
+# the desired filenam is in $env{"form.$formname.filename"}
+# $coursedoc - if true up to the current course
+# if false
+# $subdir - directory in userfile to store the file into
+# $parser, $allfiles, $codebase - unknown
+#
+# output: url of file in userspace, or error:
+# or /adm/notfound.html if failure to upload occurse
sub userfileupload {
- my ($formname,$coursedoc,$subdir,$parser,$allfiles,$codebase)=@_;
+ my ($formname,$coursedoc,$subdir,$parser,$allfiles,$codebase,$destuname,$destudom)=@_;
if (!defined($subdir)) { $subdir='unknown'; }
my $fname=$env{'form.'.$formname.'.filename'};
$fname=&clean_filename($fname);
@@ -1306,8 +1424,24 @@ sub userfileupload {
open(my $fh,'>'.$fullpath.'/'.$fname);
print $fh $env{'form.'.$formname};
close($fh);
- return $fullpath.'/'.$fname;
+ return $fullpath.'/'.$fname;
+ } elsif (($formname eq 'coursecreatorxml') && ($subdir eq 'batchupload')) { #files uploaded to create course page are handled differently
+ my $filepath = 'tmp/addcourse/'.$destudom.'/web/'.$env{'user.name'}.
+ '_'.$env{'user.domain'}.'/pending';
+ my @parts=split(/\//,$filepath);
+ my $fullpath = $perlvar{'lonDaemons'};
+ for (my $i=0;$i<@parts;$i++) {
+ $fullpath .= '/'.$parts[$i];
+ if ((-e $fullpath)!=1) {
+ mkdir($fullpath,0777);
+ }
+ }
+ open(my $fh,'>'.$fullpath.'/'.$fname);
+ print $fh $env{'form.'.$formname};
+ close($fh);
+ return $fullpath.'/'.$fname;
}
+
# Create the directory if not present
$fname="$subdir/$fname";
if ($coursedoc) {
@@ -1323,9 +1457,19 @@ sub userfileupload {
$fname,$formname,$parser,
$allfiles,$codebase);
}
+ } elsif (defined($destuname)) {
+ my $docuname=$destuname;
+ my $docudom=$destudom;
+ return &finishuserfileupload($docuname,$docudom,$formname,
+ $fname,$parser,$allfiles,$codebase);
+
} else {
my $docuname=$env{'user.name'};
my $docudom=$env{'user.domain'};
+ if (exists($env{'form.group'})) {
+ $docuname=$env{'course.'.$env{'request.course.id'}.'.num'};
+ $docudom=$env{'course.'.$env{'request.course.id'}.'.domain'};
+ }
return &finishuserfileupload($docuname,$docudom,$formname,
$fname,$parser,$allfiles,$codebase);
}
@@ -1351,8 +1495,16 @@ sub finishuserfileupload {
}
# Save the file
{
- open(FH,'>'.$filepath.'/'.$file);
- print FH $env{'form.'.$formname};
+ if (!open(FH,'>'.$filepath.'/'.$file)) {
+ &logthis('Failed to create '.$filepath.'/'.$file);
+ print STDERR ('Failed to create '.$filepath.'/'.$file."\n");
+ return '/adm/notfound.html';
+ }
+ if (!print FH ($env{'form.'.$formname})) {
+ &logthis('Failed to write to '.$filepath.'/'.$file);
+ print STDERR ('Failed to write to '.$filepath.'/'.$file."\n");
+ return '/adm/notfound.html';
+ }
close(FH);
}
if ($parser eq 'parse') {
@@ -1551,11 +1703,11 @@ sub flushcourselogs {
if ($courseidbuffer{$coursehombuf{$crsid}}) {
$courseidbuffer{$coursehombuf{$crsid}}.='&'.
&escape($crsid).'='.&escape($coursedescrbuf{$crsid}).
- ':'.&escape($courseinstcodebuf{$crsid}).':'.&escape($courseownerbuf{$crsid});
+ ':'.&escape($courseinstcodebuf{$crsid}).':'.&escape($courseownerbuf{$crsid}).':'.&escape($coursetypebuf{$crsid});
} else {
$courseidbuffer{$coursehombuf{$crsid}}=
&escape($crsid).'='.&escape($coursedescrbuf{$crsid}).
- ':'.&escape($courseinstcodebuf{$crsid}).':'.&escape($courseownerbuf{$crsid});
+ ':'.&escape($courseinstcodebuf{$crsid}).':'.&escape($courseownerbuf{$crsid}).':'.&escape($coursetypebuf{$crsid});
}
}
#
@@ -1658,6 +1810,8 @@ sub courselog {
$env{'course.'.$env{'request.course.id'}.'.internal.coursecode'};
$courseownerbuf{$env{'request.course.id'}}=
$env{'course.'.$env{'request.course.id'}.'.internal.courseowner'};
+ $coursetypebuf{$env{'request.course.id'}}=
+ $env{'course.'.$env{'request.course.id'}.'.type'};
if (defined $courselogs{$env{'request.course.id'}}) {
$courselogs{$env{'request.course.id'}}.='&'.$what;
} else {
@@ -1757,9 +1911,6 @@ sub get_course_adv_roles {
(!$nothide{$username.':'.$domain})) { next; }
if ($role eq 'cr') { next; }
my $key=&plaintext($role);
- if ($role =~ /^cr/) {
- $key=(split('/',$role))[3];
- }
if ($section) { $key.=' (Sec/Grp '.$section.')'; }
if ($returnhash{$key}) {
$returnhash{$key}.=','.$username.':'.$domain;
@@ -1828,7 +1979,7 @@ sub courseidput {
}
sub courseiddump {
- my ($domfilter,$descfilter,$sincefilter,$instcodefilter,$ownerfilter,$coursefilter,$hostidflag,$hostidref)=@_;
+ my ($domfilter,$descfilter,$sincefilter,$instcodefilter,$ownerfilter,$coursefilter,$hostidflag,$hostidref,$typefilter,$regexp_ok)=@_;
my %returnhash=();
unless ($domfilter) { $domfilter=''; }
foreach my $tryserver (keys %libserv) {
@@ -1837,7 +1988,7 @@ sub courseiddump {
foreach (
split(/\&/,&reply('courseiddump:'.$hostdom{$tryserver}.':'.
$sincefilter.':'.&escape($descfilter).':'.
- &escape($instcodefilter).':'.&escape($ownerfilter).':'.&escape($coursefilter),
+ &escape($instcodefilter).':'.&escape($ownerfilter).':'.&escape($coursefilter).':'.&escape($typefilter).':'.&escape($regexp_ok),
$tryserver))) {
my ($key,$value)=split(/\=/,$_);
if (($key) && ($value)) {
@@ -1853,28 +2004,25 @@ sub courseiddump {
# ---------------------------------------------------------- DC e-mail
sub dcmailput {
- my ($domain,$msgid,$contents,$server)=@_;
+ my ($domain,$msgid,$message,$server)=@_;
my $status = &Apache::lonnet::critical(
- 'dcmailput:'.$domain.':'.&Apache::lonnet::escape($msgid).'='.
- &Apache::lonnet::escape($$contents{$server}),$server);
+ 'dcmailput:'.$domain.':'.&escape($msgid).'='.
+ &escape($message),$server);
return $status;
}
sub dcmaildump {
my ($dom,$startdate,$enddate,$senders) = @_;
- my %returnhash=();
- foreach my $tryserver (keys(%libserv)) {
- if ($hostdom{$tryserver} eq $dom) {
- %{$returnhash{$tryserver}}=();
- my $cmd='dcmaildump:'.$dom.':'.
- &escape($startdate).':'.&escape($enddate).':';
- my @esc_senders=map { &escape($_)} @$senders;
- $cmd.=&escape(join('&',@esc_senders));
- foreach (split(/\&/,&reply($cmd,$tryserver))) {
- my ($key,$value) = split(/\=/,$_);
- if (($key) && ($value)) {
- $returnhash{$tryserver}{&unescape($key)} = &unescape($value);
- }
+ my %returnhash=();
+ if (exists($domain_primary{$dom})) {
+ my $cmd='dcmaildump:'.$dom.':'.&escape($startdate).':'.
+ &escape($enddate).':';
+ my @esc_senders=map { &escape($_)} @$senders;
+ $cmd.=&escape(join('&',@esc_senders));
+ foreach (split(/\&/,&reply($cmd,$domain_primary{$dom}))) {
+ my ($key,$value) = split(/\=/,$_);
+ if (($key) && ($value)) {
+ $returnhash{&unescape($key)} = &unescape($value);
}
}
}
@@ -1913,7 +2061,7 @@ sub get_domain_roles {
sub get_first_access {
my ($type,$argsymb)=@_;
- my ($symb,$courseid,$udom,$uname)=&Apache::lonxml::whichuser();
+ my ($symb,$courseid,$udom,$uname)=&whichuser();
if ($argsymb) { $symb=$argsymb; }
my ($map,$id,$res)=&decode_symb($symb);
if ($type eq 'map') {
@@ -1927,7 +2075,7 @@ sub get_first_access {
sub set_first_access {
my ($type)=@_;
- my ($symb,$courseid,$udom,$uname)=&Apache::lonxml::whichuser();
+ my ($symb,$courseid,$udom,$uname)=&whichuser();
my ($map,$id,$res)=&decode_symb($symb);
if ($type eq 'map') {
$res=&symbread($map);
@@ -2506,7 +2654,7 @@ sub restore {
# ---------------------------------------------------------- Course Description
sub coursedescription {
- my $courseid=shift;
+ my ($courseid,$args)=@_;
$courseid=~s/^\///;
$courseid=~s/\_/\//g;
my ($cdomain,$cnum)=split(/\//,$courseid);
@@ -2516,13 +2664,36 @@ sub coursedescription {
# trying and trying and trying to get the course description.
my %envhash=();
my %returnhash=();
- $envhash{'course.'.$normalid.'.last_cache'}=time;
+
+ my $expiretime=600;
+ if ($env{'request.course.id'} eq $normalid) {
+ $expiretime=120;
+ }
+
+ my $prefix='course.'.$cdomain.'_'.$cnum.'.';
+ if (!$args->{'freshen_cache'}
+ && ((time-$env{$prefix.'last_cache'}) < $expiretime) ) {
+ foreach my $key (keys(%env)) {
+ next if ($key !~ /^\Q$prefix\E(.*)/);
+ my ($setting) = $1;
+ $returnhash{$setting} = $env{$key};
+ }
+ return %returnhash;
+ }
+
+ # get the data agin
+ if (!$args->{'one_time'}) {
+ $envhash{'course.'.$normalid.'.last_cache'}=time;
+ }
if ($chome ne 'no_host') {
%returnhash=&dump('environment',$cdomain,$cnum);
if (!exists($returnhash{'con_lost'})) {
$returnhash{'home'}= $chome;
$returnhash{'domain'} = $cdomain;
$returnhash{'num'} = $cnum;
+ if (!defined($returnhash{'type'})) {
+ $returnhash{'type'} = 'Course';
+ }
while (my ($name,$value) = each %returnhash) {
$envhash{'course.'.$normalid.'.'.$name}=$value;
}
@@ -2534,7 +2705,9 @@ sub coursedescription {
$envhash{'course.'.$normalid.'.num'}=$cnum;
}
}
- &appenv(%envhash);
+ if (!$args->{'one_time'}) {
+ &appenv(%envhash);
+ }
return %returnhash;
}
@@ -2577,7 +2750,7 @@ sub rolesinit {
my %allroles=();
my %allgroups=();
my $now=time;
- my $userroles="user.login.time=$now\n";
+ my %userroles = ('user.login.time' => $now);
my $group_privs;
if ($rolesdump ne '') {
@@ -2600,7 +2773,9 @@ sub rolesinit {
} else {
($trole,$tend,$tstart)=split(/_/,$role);
}
- $userroles.=&set_arearole($trole,$area,$tstart,$tend,$domain,$username);
+ my %new_role = &set_arearole($trole,$area,$tstart,$tend,$domain,
+ $username);
+ @userroles{keys(%new_role)} = @new_role{keys(%new_role)};
if (($tend!=0) && ($tend<$now)) { $trole=''; }
if (($tstart!=0) && ($tstart>$now)) { $trole=''; }
if (($area ne '') && ($trole ne '')) {
@@ -2616,19 +2791,19 @@ sub rolesinit {
}
}
}
- my ($author,$adv) = &set_userprivs(\$userroles,\%allroles,\%allgroups);
- $userroles.='user.adv='.$adv."\n".
- 'user.author='.$author."\n";
+ my ($author,$adv) = &set_userprivs(\%userroles,\%allroles,\%allgroups);
+ $userroles{'user.adv'} = $adv;
+ $userroles{'user.author'} = $author;
$env{'user.adv'}=$adv;
}
- return $userroles;
+ return \%userroles;
}
sub set_arearole {
my ($trole,$area,$tstart,$tend,$domain,$username) = @_;
# log the associated role with the area
&userrolelog($trole,$username,$domain,$area,$tstart,$tend);
- return 'user.role.'.$trole.'.'.$area.'='.$tstart.'.'.$tend."\n";
+ return ('user.role.'.$trole.'.'.$area => $tstart.'.'.$tend);
}
sub custom_roleprivs {
@@ -2696,7 +2871,7 @@ sub set_userprivs {
if (keys(%{$allgroups}) > 0) {
foreach my $role (keys %{$allroles}) {
my ($trole,$area,$sec,$extendedarea);
- if ($role =~ m|^(\w+)\.(/\w+/\w+)(/?\w*)|) {
+ if ($role =~ m-^(\w+|cr/\w+/\w+/\w+)\.(/\w+/\w+)(/?\w*)-) {
$trole = $1;
$area = $2;
$sec = $3;
@@ -2730,7 +2905,7 @@ sub set_userprivs {
}
my $thesestr='';
foreach (keys %thesepriv) { $thesestr.=':'.$_.'&'.$thesepriv{$_}; }
- $$userroles.='user.priv.'.$_.'='.$thesestr."\n";
+ $userroles->{'user.priv.'.$_} = $thesestr;
}
return ($author,$adv);
}
@@ -2781,23 +2956,32 @@ sub del {
# -------------------------------------------------------------- dump interface
sub dump {
- my ($namespace,$udomain,$uname,$regexp)=@_;
- if (!$udomain) { $udomain=$env{'user.domain'}; }
- if (!$uname) { $uname=$env{'user.name'}; }
- my $uhome=&homeserver($uname,$udomain);
- if ($regexp) {
- $regexp=&escape($regexp);
- } else {
- $regexp='.';
- }
- my $rep=reply("dump:$udomain:$uname:$namespace:$regexp",$uhome);
- my @pairs=split(/\&/,$rep);
- my %returnhash=();
- foreach (@pairs) {
- my ($key,$value)=split(/=/,$_);
- $returnhash{unescape($key)}=&thaw_unescape($value);
- }
- return %returnhash;
+ my ($namespace,$udomain,$uname,$regexp,$range)=@_;
+ if (!$udomain) { $udomain=$env{'user.domain'}; }
+ if (!$uname) { $uname=$env{'user.name'}; }
+ my $uhome=&homeserver($uname,$udomain);
+ if ($regexp) {
+ $regexp=&escape($regexp);
+ } else {
+ $regexp='.';
+ }
+ my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range",$uhome);
+ my @pairs=split(/\&/,$rep);
+ my %returnhash=();
+ foreach my $item (@pairs) {
+ my ($key,$value)=split(/=/,$item,2);
+ $key = &unescape($key);
+ next if ($key =~ /^error: 2 /);
+ $returnhash{$key}=&thaw_unescape($value);
+ }
+ return %returnhash;
+}
+
+# --------------------------------------------------------- dumpstore interface
+
+sub dumpstore {
+ my ($namespace,$udomain,$uname,$regexp,$range)=@_;
+ return &dump($namespace,$udomain,$uname,$regexp,$range);
}
# -------------------------------------------------------------- keys interface
@@ -2941,25 +3125,53 @@ sub newput {
# --------------------------------------------------------- putstore interface
sub putstore {
- my ($namespace,$storehash,$udomain,$uname)=@_;
+ my ($namespace,$symb,$version,$storehash,$udomain,$uname)=@_;
if (!$udomain) { $udomain=$env{'user.domain'}; }
if (!$uname) { $uname=$env{'user.name'}; }
my $uhome=&homeserver($uname,$udomain);
my $items='';
- my %allitems = ();
- foreach (keys %$storehash) {
- if ($_ =~ m/^([^\:]+):([^\:]+):([^\:]+)$/) {
- my $key = $1.':keys:'.$2;
- $allitems{$key} .= $3.':';
- }
- $items.=$_.'='.&freeze_escape($$storehash{$_}).'&';
- }
- foreach (keys %allitems) {
- $allitems{$_} =~ s/\:$//;
- $items.= $_.'='.$allitems{$_}.'&';
+ foreach my $key (keys(%$storehash)) {
+ $items.= &escape($key).'='.&freeze_escape($storehash->{$key}).'&';
}
$items=~s/\&$//;
- return &reply("put:$udomain:$uname:$namespace:$items",$uhome);
+ my $esc_symb=&escape($symb);
+ my $esc_v=&escape($version);
+ my $reply =
+ &reply("putstore:$udomain:$uname:$namespace:$esc_symb:$esc_v:$items",
+ $uhome);
+ if ($reply eq 'unknown_cmd') {
+ # gfall back to way things use to be done
+ return &old_putstore($namespace,$symb,$version,$storehash,$udomain,
+ $uname);
+ }
+ return $reply;
+}
+
+sub old_putstore {
+ my ($namespace,$symb,$version,$storehash,$udomain,$uname)=@_;
+ if (!$udomain) { $udomain=$env{'user.domain'}; }
+ if (!$uname) { $uname=$env{'user.name'}; }
+ my $uhome=&homeserver($uname,$udomain);
+ my %newstorehash;
+ foreach (keys %$storehash) {
+ my $key = $version.':'.&escape($symb).':'.$_;
+ $newstorehash{$key} = $storehash->{$_};
+ }
+ my $items='';
+ my %allitems = ();
+ foreach (keys %newstorehash) {
+ if ($_ =~ m/^([^\:]+):([^\:]+):([^\:]+)$/) {
+ my $key = $1.':keys:'.$2;
+ $allitems{$key} .= $3.':';
+ }
+ $items.=$_.'='.&freeze_escape($newstorehash{$_}).'&';
+ }
+ foreach (keys %allitems) {
+ $allitems{$_} =~ s/\:$//;
+ $items.= $_.'='.$allitems{$_}.'&';
+ }
+ $items=~s/\&$//;
+ return &reply("put:$udomain:$uname:$namespace:$items",$uhome);
}
# ------------------------------------------------------ critical put interface
@@ -2971,7 +3183,7 @@ sub cput {
my $uhome=&homeserver($uname,$udomain);
my $items='';
foreach (keys %$storehash) {
- $items.=escape($_).'='.&freeze_escape($$storehash{$_}).'&';
+ $items.=&escape($_).'='.&freeze_escape($$storehash{$_}).'&';
}
$items=~s/\&$//;
return &critical("put:$udomain:$uname:$namespace:$items",$uhome);
@@ -3013,8 +3225,9 @@ sub tmpput {
# ------------------------------------------------------------ tmpget interface
sub tmpget {
- my ($token)=@_;
- my $rep=&reply("tmpget:$token",$perlvar{'lonHostID'});
+ my ($token,$server)=@_;
+ if (!defined($server)) { $server = $perlvar{'lonHostID'}; }
+ my $rep=&reply("tmpget:$token",$server);
my %returnhash;
foreach my $item (split(/\&/,$rep)) {
my ($key,$value)=split(/=/,$item);
@@ -3023,6 +3236,225 @@ sub tmpget {
return %returnhash;
}
+# ------------------------------------------------------------ tmpget interface
+sub tmpdel {
+ my ($token,$server)=@_;
+ if (!defined($server)) { $server = $perlvar{'lonHostID'}; }
+ return &reply("tmpdel:$token",$server);
+}
+
+# -------------------------------------------------- portfolio access checking
+
+sub portfolio_access {
+ my ($requrl) = @_;
+ my (undef,$udom,$unum,$file_name,$group) = &parse_portfolio_url($requrl);
+ my $result = &get_portfolio_access($udom,$unum,$file_name,$group);
+ if ($result eq 'ok') {
+ return 'F';
+ } elsif ($result =~ /^[^:]+:guest_/) {
+ return 'A';
+ }
+ return '';
+}
+
+sub get_portfolio_access {
+ my ($udom,$unum,$file_name,$group,$access_hash) = @_;
+
+ if (!ref($access_hash)) {
+ my $current_perms = &get_portfile_permissions($udom,$unum);
+ my %access_controls = &get_access_controls($current_perms,$group,
+ $file_name);
+ $access_hash = $access_controls{$file_name};
+ }
+
+ my ($public,$guest,@domains,@users,@courses,@groups);
+ 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*)$/);
+ if ($start > $now) {
+ next;
+ }
+ if ($end && $end<$now) {
+ next;
+ }
+ if ($scope eq 'public') {
+ $public = $key;
+ last;
+ } elsif ($scope eq 'guest') {
+ $guest = $key;
+ } elsif ($scope eq 'domains') {
+ push(@domains,$key);
+ } elsif ($scope eq 'users') {
+ push(@users,$key);
+ } elsif ($scope eq 'course') {
+ push(@courses,$key);
+ } elsif ($scope eq 'group') {
+ push(@groups,$key);
+ }
+ }
+ if ($public) {
+ return 'ok';
+ }
+ if ($env{'user.name'} eq 'public' && $env{'user.domain'} eq 'public') {
+ if ($guest) {
+ return $guest;
+ }
+ } else {
+ if (@domains > 0) {
+ foreach my $domkey (@domains) {
+ if (ref($access_hash->{$domkey}{'dom'}) eq 'ARRAY') {
+ if (grep(/^\Q$env{'user.domain'}\E$/,@{$access_hash->{$domkey}{'dom'}})) {
+ return 'ok';
+ }
+ }
+ }
+ }
+ if (@users > 0) {
+ foreach my $userkey (@users) {
+ if (exists($access_hash->{$userkey}{'users'}{$env{'user.name'}.':'.$env{'user.domain'}})) {
+ return 'ok';
+ }
+ }
+ }
+ my %roleshash;
+ my @courses_and_groups = @courses;
+ push(@courses_and_groups,@groups);
+ if (@courses_and_groups > 0) {
+ my (%allgroups,%allroles);
+ my ($start,$end,$role,$sec,$group);
+ foreach my $envkey (%env) {
+ if ($envkey =~ m-^user\.role\.(gr|cc|in|ta|ep|st)\./([^/]+)/([^/]+)/?([^/]*)$-) {
+ my $cid = $2.'_'.$3;
+ if ($1 eq 'gr') {
+ $group = $4;
+ $allgroups{$cid}{$group} = $env{$envkey};
+ } else {
+ if ($4 eq '') {
+ $sec = 'none';
+ } else {
+ $sec = $4;
+ }
+ $allroles{$cid}{$1}{$sec} = $env{$envkey};
+ }
+ } elsif ($envkey =~ m-^user\.role\./cr/(\w+/\w+/\w*)./([^/]+)/([^/]+)/?([^/]*)$-) {
+ my $cid = $2.'_'.$3;
+ if ($4 eq '') {
+ $sec = 'none';
+ } else {
+ $sec = $4;
+ }
+ $allroles{$cid}{$1}{$sec} = $env{$envkey};
+ }
+ }
+ if (keys(%allroles) == 0) {
+ return;
+ }
+ foreach my $key (@courses_and_groups) {
+ my %content = %{$$access_hash{$key}};
+ my $cnum = $content{'number'};
+ my $cdom = $content{'domain'};
+ my $cid = $cdom.'_'.$cnum;
+ if (!exists($allroles{$cid})) {
+ next;
+ }
+ foreach my $role_id (keys(%{$content{'roles'}})) {
+ my @sections = @{$content{'roles'}{$role_id}{'section'}};
+ my @groups = @{$content{'roles'}{$role_id}{'group'}};
+ my @status = @{$content{'roles'}{$role_id}{'access'}};
+ my @roles = @{$content{'roles'}{$role_id}{'role'}};
+ foreach my $role (keys(%{$allroles{$cid}})) {
+ if ((grep/^all$/,@roles) || (grep/^\Q$role\E$/,@roles)) {
+ foreach my $sec (keys(%{$allroles{$cid}{$role}})) {
+ if (&course_group_datechecker($allroles{$cid}{$role}{$sec},$now,\@status) eq 'ok') {
+ if (grep/^all$/,@sections) {
+ return 'ok';
+ } else {
+ if (grep/^$sec$/,@sections) {
+ return 'ok';
+ }
+ }
+ }
+ }
+ if (keys(%{$allgroups{$cid}}) == 0) {
+ if (grep/^none$/,@groups) {
+ return 'ok';
+ }
+ } else {
+ if (grep/^all$/,@groups) {
+ return 'ok';
+ }
+ foreach my $group (keys(%{$allgroups{$cid}})) {
+ if (grep/^$group$/,@groups) {
+ return 'ok';
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if ($guest) {
+ return $guest;
+ }
+ }
+ }
+ return;
+}
+
+sub course_group_datechecker {
+ my ($dates,$now,$status) = @_;
+ my ($start,$end) = split(/\./,$dates);
+ if (!$start && !$end) {
+ return 'ok';
+ }
+ if (grep/^active$/,@{$status}) {
+ if (((!$start) || ($start && $start <= $now)) && ((!$end) || ($end && $end >= $now))) {
+ return 'ok';
+ }
+ }
+ if (grep/^previous$/,@{$status}) {
+ if ($end > $now ) {
+ return 'ok';
+ }
+ }
+ if (grep/^future$/,@{$status}) {
+ if ($start > $now) {
+ return 'ok';
+ }
+ }
+ return;
+}
+
+sub parse_portfolio_url {
+ my ($url) = @_;
+
+ my ($type,$udom,$unum,$group,$file_name);
+
+ if ($url =~ m-^/*uploaded/([^/]+)/([^/]+)/portfolio(/.+)$-) {
+ $type = 1;
+ $udom = $1;
+ $unum = $2;
+ $file_name = $3;
+ } elsif ($url =~ m-^/*uploaded/([^/]+)/([^/]+)/groups/([^/]+)/portfolio/(.+)$-) {
+ $type = 2;
+ $udom = $1;
+ $unum = $2;
+ $group = $3;
+ $file_name = $3.'/'.$4;
+ }
+ if (wantarray) {
+ return ($type,$udom,$unum,$file_name,$group);
+ }
+ return $type;
+}
+
+sub is_portfolio_url {
+ my ($url) = @_;
+ return scalar(&parse_portfolio_url($url));
+}
+
# ---------------------------------------------- Custom access rule evaluation
sub customaccess {
@@ -3061,24 +3493,51 @@ sub customaccess {
sub allowed {
my ($priv,$uri,$symb)=@_;
+ my $ver_orguri=$uri;
$uri=&deversion($uri);
my $orguri=$uri;
$uri=&declutter($uri);
if (defined($env{'allowed.'.$priv})) { return $env{'allowed.'.$priv}; }
# Free bre access to adm and meta resources
- if (((($uri=~/^adm\//) && ($uri !~ m|/bulletinboard$|))
- || ($uri=~/\.meta$/)) && ($priv eq 'bre')) {
+ if (((($uri=~/^adm\//) && ($uri !~ m{/(?:smppg|bulletinboard)$}))
+ || (($uri=~/\.meta$/) && ($uri!~m|^uploaded/|) ))
+ && ($priv eq 'bre')) {
return 'F';
}
# Free bre access to user's own portfolio contents
- my ($space,$domain,$name,$dir)=split('/',$uri);
+ my ($space,$domain,$name,@dir)=split('/',$uri);
if (($space=~/^(uploaded|editupload)$/) && ($env{'user.name'} eq $name) &&
- ($env{'user.domain'} eq $domain) && ('portfolio' eq $dir)) {
+ ($env{'user.domain'} eq $domain) && ('portfolio' eq $dir[0])) {
return 'F';
}
+# bre access to group portfolio for rgf priv in group, or mdg or vcg in course.
+ if (($space=~/^(uploaded|editupload)$/) && ($dir[0] eq 'groups')
+ && ($dir[2] eq 'portfolio') && ($priv eq 'bre')) {
+ if (exists($env{'request.course.id'})) {
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ if (($domain eq $cdom) && ($name eq $cnum)) {
+ my $courseprivid=$env{'request.course.id'};
+ $courseprivid=~s/\_/\//;
+ if ($env{'user.priv.'.$env{'request.role'}.'./'.$courseprivid
+ .'/'.$dir[1]} =~/rgf\&([^\:]*)/) {
+ return $1;
+ } else {
+ if ($env{'request.course.sec'}) {
+ $courseprivid.='/'.$env{'request.course.sec'};
+ }
+ if ($env{'user.priv.'.$env{'request.role'}.'./'.
+ $courseprivid} =~/(mdg|vcg)\&([^\:]*)/) {
+ return $2;
+ }
+ }
+ }
+ }
+ }
+
# Free bre to public access
if ($priv eq 'bre') {
@@ -3142,14 +3601,6 @@ sub allowed {
$thisallowed.=$1;
}
-# Group: uri itself is a group
- my $groupuri=$uri;
- $groupuri=~s/^([^\/])/\/$1/;
- if ($env{'user.priv.'.$env{'request.role'}.'.'.$groupuri}
- =~/\Q$priv\E\&([^\:]*)/) {
- $thisallowed.=$1;
- }
-
# URI is an uploaded document for this course, default permissions don't matter
# not allowing 'edit' access (editupload) to uploaded course docs
if (($priv eq 'bre') && ($uri=~m|^uploaded/|)) {
@@ -3161,7 +3612,7 @@ sub allowed {
$thisallowed.=$1;
}
} else {
- my $refuri=$env{'httpref.'.$orguri};
+ my $refuri = $env{'httpref.'.$orguri} || $env{'httpref.'.$ver_orguri};
if ($refuri) {
if ($refuri =~ m|^/adm/|) {
$thisallowed='F';
@@ -3176,6 +3627,13 @@ sub allowed {
}
}
+ if ($priv eq 'bre'
+ && $thisallowed ne 'F'
+ && $thisallowed ne '2'
+ && &is_portfolio_url($uri)) {
+ $thisallowed = &portfolio_access($uri);
+ }
+
# Full access at system, domain or course-wide level? Exit.
if ($thisallowed=~/F/) {
@@ -3293,7 +3751,7 @@ sub allowed {
my ($cdom,$cnum,$csec)=split(/\//,$courseid);
my $prefix='course.'.$cdom.'_'.$cnum.'.';
if ((time-$env{$prefix.'last_cache'})>$expiretime) {
- &coursedescription($courseid);
+ &coursedescription($courseid,{'freshen_cache' => 1});
}
if (($env{$prefix.'res.'.$uri.'.lock.sections'}=~/\,\Q$csec\E\,/)
|| ($env{$prefix.'res.'.$uri.'.lock.sections'} eq 'all')) {
@@ -3326,7 +3784,11 @@ sub allowed {
#
unless ($env{'request.course.id'}) {
- return '1';
+ if ($thisallowed eq 'A') {
+ return 'A';
+ } else {
+ return '1';
+ }
}
#
@@ -3341,17 +3803,21 @@ sub allowed {
my $unamedom=$env{'user.name'}.':'.$env{'user.domain'};
if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.roles.denied'}
=~/\Q$rolecode\E/) {
- &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.':'.
- 'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode.' in '.
- $env{'request.course.id'});
+ if ($priv ne 'pch') {
+ &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.':'.
+ 'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode.' in '.
+ $env{'request.course.id'});
+ }
return '';
}
if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.users.denied'}
=~/\Q$unamedom\E/) {
- &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.
- 'Denied by user: '.$priv.' for '.$uri.' as '.$unamedom.' in '.
- $env{'request.course.id'});
+ if ($priv ne 'pch') {
+ &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.
+ 'Denied by user: '.$priv.' for '.$uri.' as '.$unamedom.' in '.
+ $env{'request.course.id'});
+ }
return '';
}
}
@@ -3361,9 +3827,11 @@ sub allowed {
if ($thisallowed=~/R/) {
my $rolecode=(split(/\./,$env{'request.role'}))[0];
if (&metadata($uri,'roledeny')=~/\Q$rolecode\E/) {
- &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.':'.
- 'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode);
- return '';
+ if ($priv ne 'pch') {
+ &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.':'.
+ 'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode);
+ }
+ return '';
}
}
@@ -3383,18 +3851,23 @@ sub allowed {
}
}
+ if ($thisallowed eq 'A') {
+ return 'A';
+ }
return 'F';
}
+sub split_uri_for_cond {
+ my $uri=&deversion(&declutter(shift));
+ my @uriparts=split(/\//,$uri);
+ my $filename=pop(@uriparts);
+ my $pathname=join('/',@uriparts);
+ return ($pathname,$filename);
+}
# --------------------------------------------------- Is a resource on the map?
sub is_on_map {
- my $uri=&deversion(&declutter(shift));
- my @uriparts=split(/\//,$uri);
- my $filename=$uriparts[$#uriparts];
- my $pathname=$uri;
- $pathname=~s|/\Q$filename\E$||;
- $pathname=~s/^adm\/wrapper\///;
+ my ($pathname,$filename) = &split_uri_for_cond(shift);
#Trying to find the conditional for the file
my $match=($env{'acc.res.'.$env{'request.course.id'}.'.'.$pathname}=~
/\&\Q$filename\E\:([\d\|]+)\&/);
@@ -3627,7 +4100,7 @@ sub auto_run {
my $response = &reply('autorun:'.$cdom,$homeserver);
return $response;
}
-
+
sub auto_get_sections {
my ($cnum,$cdom,$inst_coursecode) = @_;
my $homeserver = &homeserver($cnum,$cdom);
@@ -3638,21 +4111,21 @@ sub auto_get_sections {
}
return @secs;
}
-
+
sub auto_new_course {
my ($cnum,$cdom,$inst_course_id,$owner) = @_;
my $homeserver = &homeserver($cnum,$cdom);
my $response=&unescape(&reply('autonewcourse:'.$inst_course_id.':'.$owner.':'.$cdom,$homeserver));
return $response;
}
-
+
sub auto_validate_courseID {
my ($cnum,$cdom,$inst_course_id) = @_;
my $homeserver = &homeserver($cnum,$cdom);
my $response=&unescape(&reply('autovalidatecourse:'.$inst_course_id.':'.$cdom,$homeserver));
return $response;
}
-
+
sub auto_create_password {
my ($cnum,$cdom,$authparam) = @_;
my $homeserver = &homeserver($cnum,$cdom);
@@ -3667,36 +4140,128 @@ sub auto_create_password {
return ($authparam,$create_passwd,$authchk);
}
+sub auto_photo_permission {
+ my ($cnum,$cdom,$students) = @_;
+ my $homeserver = &homeserver($cnum,$cdom);
+ my ($outcome,$perm_reqd,$conditions) =
+ split(/:/,&unescape(&reply('autophotopermission:'.$cdom,$homeserver)),3);
+ if ($outcome =~ /^(con_lost|unknown_cmd|no_such_host)$/) {
+ return (undef,undef);
+ }
+ return ($outcome,$perm_reqd,$conditions);
+}
+
+sub auto_checkphotos {
+ my ($uname,$udom,$pid) = @_;
+ my $homeserver = &homeserver($uname,$udom);
+ my ($result,$resulttype);
+ my $outcome = &unescape(&reply('autophotocheck:'.&escape($udom).':'.
+ &escape($uname).':'.&escape($pid),
+ $homeserver));
+ if ($outcome =~ /^(con_lost|unknown_cmd|no_such_host)$/) {
+ return (undef,undef);
+ }
+ if ($outcome) {
+ ($result,$resulttype) = split(/:/,$outcome);
+ }
+ return ($result,$resulttype);
+}
+
+sub auto_photochoice {
+ my ($cnum,$cdom) = @_;
+ my $homeserver = &homeserver($cnum,$cdom);
+ my ($update,$comment) = split(/:/,&unescape(&reply('autophotochoice:'.
+ &escape($cdom),
+ $homeserver)));
+ if ($update =~ /^(con_lost|unknown_cmd|no_such_host)$/) {
+ return (undef,undef);
+ }
+ return ($update,$comment);
+}
+
+sub auto_photoupdate {
+ my ($affiliatesref,$dom,$cnum,$photo) = @_;
+ my $homeserver = &homeserver($cnum,$dom);
+ my $host=$hostname{$homeserver};
+ my $cmd = '';
+ my $maxtries = 1;
+ foreach (keys %{$affiliatesref}) {
+ $cmd .= $_.'='.join(",",@{$$affiliatesref{$_}}).'%%';
+ }
+ $cmd =~ s/%%$//;
+ $cmd = &escape($cmd);
+ my $query = 'institutionalphotos';
+ my $queryid=&reply("querysend:".$query.':'.$dom.':'.$cnum.':'.$cmd,$homeserver);
+ unless ($queryid=~/^\Q$host\E\_/) {
+ &logthis('institutionalphotos: invalid queryid: '.$queryid.' for host: '.$host.' and homeserver: '.$homeserver.' and course: '.$cnum);
+ return 'error: '.$queryid;
+ }
+ my $reply = &get_query_reply($queryid);
+ my $tries = 1;
+ while (($reply=~/^timeout/) && ($tries < $maxtries)) {
+ $reply = &get_query_reply($queryid);
+ $tries ++;
+ }
+ if ( ($reply =~/^timeout/) || ($reply =~/^error/) ) {
+ &logthis('institutionalphotos error: '.$reply.' for '.$dom.' '.$env{'user.name'}.' for '.$queryid.' course: '.$cnum.' maxtries: '.$maxtries.' tries: '.$tries);
+ } else {
+ my @responses = split(/:/,$reply);
+ my $outcome = shift(@responses);
+ foreach my $item (@responses) {
+ my ($key,$value) = split(/=/,$item);
+ $$photo{$key} = $value;
+ }
+ return $outcome;
+ }
+ return 'error';
+}
+
sub auto_instcode_format {
my ($caller,$codedom,$instcodes,$codes,$codetitles,$cat_titles,$cat_order) = @_;
my $courses = '';
- my $homeserver;
+ my @homeservers;
if ($caller eq 'global') {
foreach my $tryserver (keys %libserv) {
if ($hostdom{$tryserver} eq $codedom) {
- $homeserver = $tryserver;
- last;
+ if (!grep/^\Q$tryserver\E$/,@homeservers) {
+ push(@homeservers,$tryserver);
+ }
}
}
- if (($env{'user.name'}) && ($env{'user.domain'} eq $codedom)) {
- $homeserver = &homeserver($env{'user.name'},$codedom);
- }
} else {
- $homeserver = &homeserver($caller,$codedom);
+ push(@homeservers,&homeserver($caller,$codedom));
}
foreach (keys %{$instcodes}) {
$courses .= &escape($_).'='.&escape($$instcodes{$_}).'&';
}
chop($courses);
- my $response=&reply('autoinstcodeformat:'.$codedom.':'.$courses,$homeserver);
- unless ($response =~ /(con_lost|error|no_such_host|refused)/) {
- my ($codes_str,$codetitles_str,$cat_titles_str,$cat_order_str) = split/:/,$response;
- %{$codes} = &str2hash($codes_str);
- @{$codetitles} = &str2array($codetitles_str);
- %{$cat_titles} = &str2hash($cat_titles_str);
- %{$cat_order} = &str2hash($cat_order_str);
+ my $ok_response = 0;
+ my $response;
+ while (@homeservers > 0 && $ok_response == 0) {
+ my $server = shift(@homeservers);
+ $response=&reply('autoinstcodeformat:'.$codedom.':'.$courses,$server);
+ if ($response !~ /(con_lost|error|no_such_host|refused)/) {
+ my ($codes_str,$codetitles_str,$cat_titles_str,$cat_order_str) =
+ split/:/,$response;
+ %{$codes} = (%{$codes},&str2hash($codes_str));
+ push(@{$codetitles},&str2array($codetitles_str));
+ %{$cat_titles} = (%{$cat_titles},&str2hash($cat_titles_str));
+ %{$cat_order} = (%{$cat_order},&str2hash($cat_order_str));
+ $ok_response = 1;
+ }
+ }
+ if ($ok_response) {
return 'ok';
+ } else {
+ return $response;
}
+}
+
+sub auto_validate_class_sec {
+ my ($cdom,$cnum,$owner,$inst_class) = @_;
+ my $homeserver = &homeserver($cnum,$cdom);
+ my $response=&reply('autovalidateclass_sec:'.$inst_class.':'.
+ &escape($owner).':'.$cdom,$homeserver);
return $response;
}
@@ -3718,6 +4283,9 @@ sub modify_group_roles {
my $role = 'gr/'.&escape($userprivs);
my ($uname,$udom) = split(/:/,$user);
my $result = &assignrole($udom,$uname,$url,$role,$end,$start);
+ if ($result eq 'ok') {
+ &devalidate_getgroups_cache($udom,$uname,$cdom,$cnum);
+ }
return $result;
}
@@ -3751,31 +4319,51 @@ sub get_group_membership {
sub get_users_groups {
my ($udom,$uname,$courseid) = @_;
+ my @usersgroups;
my $cachetime=1800;
$courseid=~s/\_/\//g;
$courseid=~s/^(\w)/\/$1/;
my $hashid="$udom:$uname:$courseid";
- my ($result,$cached)=&is_cached_new('getgroups',$hashid);
- if (defined($cached)) { return $result; }
-
- my %roleshash = &dump('roles',$udom,$uname,$courseid);
- my ($tmp) = keys(%roleshash);
- if ($tmp=~/^error:/) {
- &logthis('Error retrieving roles: '.$tmp.' for '.$uname.':'.$udom);
- return '';
- } else {
- my $grouplist;
- foreach my $key (keys %roleshash) {
- if ($key =~ /^\Q$courseid\E\/(\w+)\_gr$/) {
- unless ($roleshash{$key} =~ /_1_1$/) { # deleted membership
- $grouplist .= $1.':';
+ my ($grouplist,$cached)=&is_cached_new('getgroups',$hashid);
+ if (defined($cached)) {
+ @usersgroups = split(/:/,$grouplist);
+ } else {
+ $grouplist = '';
+ my %roleshash = &dump('roles',$udom,$uname,$courseid);
+ my ($tmp) = keys(%roleshash);
+ if ($tmp=~/^error:/) {
+ &logthis('Error retrieving roles: '.$tmp.' for '.$uname.':'.$udom);
+ } else {
+ my $access_end = $env{'course.'.$courseid.
+ '.default_enrollment_end_date'};
+ my $now = time;
+ foreach my $key (keys(%roleshash)) {
+ if ($key =~ /^\Q$courseid\E\/(\w+)\_gr$/) {
+ my $group = $1;
+ if ($roleshash{$key} =~ /_(\d+)_(\d+)$/) {
+ my $start = $2;
+ my $end = $1;
+ if ($start == -1) { next; } # deleted from group
+ if (($start!=0) && ($start>$now)) { next; }
+ if (($end!=0) && ($end<$now)) {
+ if ($access_end && $access_end < $now) {
+ if ($access_end - $end < 86400) {
+ push(@usersgroups,$group);
+ }
+ }
+ next;
+ }
+ push(@usersgroups,$group);
+ }
}
}
+ @usersgroups = &sort_course_groups($courseid,@usersgroups);
+ $grouplist = join(':',@usersgroups);
+ &do_cache_new('getgroups',$hashid,$grouplist,$cachetime);
}
- $grouplist =~ s/:$//;
- return &do_cache_new('getgroups',$hashid,$grouplist,$cachetime);
}
+ return @usersgroups;
}
sub devalidate_getgroups_cache {
@@ -3790,8 +4378,28 @@ sub devalidate_getgroups_cache {
# ------------------------------------------------------------------ Plain Text
sub plaintext {
- my $short=shift;
- return &Apache::lonlocal::mt($prp{$short});
+ my ($short,$type,$cid) = @_;
+ if ($short =~ /^cr/) {
+ return (split('/',$short))[-1];
+ }
+ if (!defined($cid)) {
+ $cid = $env{'request.course.id'};
+ }
+ if (defined($cid) && defined($env{'course.'.$cid.'.'.$short.'.plaintext'})) {
+ return &Apache::lonlocal::mt($env{'course.'.$cid.'.'.$short.
+ '.plaintext'});
+ }
+ my %rolenames = (
+ Course => 'std',
+ Group => 'alt1',
+ );
+ if (defined($type) &&
+ defined($rolenames{$type}) &&
+ defined($prp{$short}{$rolenames{$type}})) {
+ return &Apache::lonlocal::mt($prp{$short}{$rolenames{$type}});
+ } else {
+ return &Apache::lonlocal::mt($prp{$short}{'std'});
+ }
}
# ----------------------------------------------------------------- Assign Role
@@ -3840,6 +4448,8 @@ sub assignrole {
$command.='_0_'.$start;
}
}
+ my $origstart = $start;
+ my $origend = $end;
# actually delete
if ($deleteflag) {
if ((&allowed('dro',$udom)) || (&allowed('dro',$url))) {
@@ -3857,6 +4467,11 @@ sub assignrole {
# log new user role if status is ok
if ($answer eq 'ok') {
&userrolelog($role,$uname,$udom,$url,$start,$end);
+# for course roles, perform group memberships changes triggered by role change.
+ unless ($role =~ /^gr/) {
+ &Apache::longroup::group_changes($udom,$uname,$url,$role,$origend,
+ $origstart);
+ }
}
return $answer;
}
@@ -4113,7 +4728,8 @@ sub writecoursepref {
# ---------------------------------------------------------- Make/modify course
sub createcourse {
- my ($udom,$description,$url,$course_server,$nonstandard,$inst_code,$course_owner)=@_;
+ my ($udom,$description,$url,$course_server,$nonstandard,$inst_code,
+ $course_owner,$crstype)=@_;
$url=&declutter($url);
my $cid='';
unless (&allowed('ccc',$udom)) {
@@ -4150,7 +4766,8 @@ sub createcourse {
# ----------------------------------------------------------------- Course made
# log existence
&courseidput($udom,&escape($udom.'_'.$uname).'='.&escape($description).
- ':'.&escape($inst_code).':'.&escape($course_owner),$uhome);
+ ':'.&escape($inst_code).':'.&escape($course_owner).':'.
+ &escape($crstype),$uhome);
&flushcourselogs();
# set toplevel url
my $topurl=$url;
@@ -4220,14 +4837,28 @@ sub is_locked {
$env{'user.domain'},$env{'user.name'});
my ($tmp)=keys(%locked);
if ($tmp=~/^error:/) { undef(%locked); }
-
+
if (ref($locked{$file_name}) eq 'ARRAY') {
- $is_locked = 'true';
+ $is_locked = 'false';
+ foreach my $entry (@{$locked{$file_name}}) {
+ if (ref($entry) eq 'ARRAY') {
+ $is_locked = 'true';
+ last;
+ }
+ }
} else {
$is_locked = 'false';
}
}
+sub declutter_portfile {
+ my ($file) = @_;
+ &logthis("got $file");
+ $file =~ s-^(/portfolio/|portfolio/)-/-;
+ &logthis("ret $file");
+ return $file;
+}
+
# ------------------------------------------------------------- Mark as Read Only
sub mark_as_readonly {
@@ -4236,6 +4867,7 @@ sub mark_as_readonly {
my ($tmp)=keys(%current_permissions);
if ($tmp=~/^error:/) { undef(%current_permissions); }
foreach my $file (@{$files}) {
+ $file = &declutter_portfile($file);
push(@{$current_permissions{$file}},$what);
}
&put('file_permissions',\%current_permissions,$domain,$user);
@@ -4311,49 +4943,195 @@ sub files_not_in_path {
return (@return_files);
}
-#--------------------------------------------------------------Get Marked as Read Only
-
+#----------------------------------------------Get portfolio file permissions
-sub get_marked_as_readonly {
- my ($domain,$user,$what) = @_;
+sub get_portfile_permissions {
+ my ($domain,$user) = @_;
my %current_permissions = &dump('file_permissions',$domain,$user);
my ($tmp)=keys(%current_permissions);
if ($tmp=~/^error:/) { undef(%current_permissions); }
+ return \%current_permissions;
+}
+
+#---------------------------------------------Get portfolio file access controls
+
+sub get_access_controls {
+ my ($current_permissions,$group,$file) = @_;
+ my %access;
+ my $real_file = $file;
+ $file =~ s/\.meta$//;
+ if (defined($file)) {
+ if (ref($$current_permissions{$file."\0".'accesscontrol'}) eq 'HASH') {
+ foreach my $control (keys(%{$$current_permissions{$file."\0".'accesscontrol'}})) {
+ $access{$real_file}{$control} = $$current_permissions{$file."\0".$control};
+ }
+ }
+ } else {
+ foreach my $key (keys(%{$current_permissions})) {
+ if ($key =~ /\0accesscontrol$/) {
+ if (defined($group)) {
+ if ($key !~ m-^\Q$group\E/-) {
+ next;
+ }
+ }
+ my ($fullpath) = split(/\0/,$key);
+ if (ref($$current_permissions{$key}) eq 'HASH') {
+ foreach my $control (keys(%{$$current_permissions{$key}})) {
+ $access{$fullpath}{$control}=$$current_permissions{$fullpath."\0".$control};
+ }
+ }
+ }
+ }
+ }
+ return %access;
+}
+
+sub modify_access_controls {
+ my ($file_name,$changes,$domain,$user)=@_;
+ my ($outcome,$deloutcome);
+ my %store_permissions;
+ my %new_values;
+ my %new_control;
+ my %translation;
+ my @deletions = ();
+ my $now = time;
+ if (exists($$changes{'activate'})) {
+ if (ref($$changes{'activate'}) eq 'HASH') {
+ my @newitems = sort(keys(%{$$changes{'activate'}}));
+ my $numnew = scalar(@newitems);
+ for (my $i=0; $i<$numnew; $i++) {
+ my $newkey = $newitems[$i];
+ my $newid = &Apache::loncommon::get_cgi_id();
+ $newkey =~ s/^(\d+)/$newid/;
+ $translation{$1} = $newid;
+ $new_values{$file_name."\0".$newkey} =
+ $$changes{'activate'}{$newitems[$i]};
+ $new_control{$newkey} = $now;
+ }
+ }
+ }
+ my %todelete;
+ my %changed_items;
+ foreach my $action ('delete','update') {
+ if (exists($$changes{$action})) {
+ if (ref($$changes{$action}) eq 'HASH') {
+ foreach my $key (keys(%{$$changes{$action}})) {
+ my ($itemnum) = ($key =~ /^([^:]+):/);
+ if ($action eq 'delete') {
+ $todelete{$itemnum} = 1;
+ } else {
+ $changed_items{$itemnum} = $key;
+ }
+ }
+ }
+ }
+ }
+ # get lock on access controls for file.
+ my $lockhash = {
+ $file_name."\0".'locked_access_records' => $env{'user.name'}.
+ ':'.$env{'user.domain'},
+ };
+ my $tries = 0;
+ my $gotlock = &newput('file_permissions',$lockhash,$domain,$user);
+
+ while (($gotlock ne 'ok') && $tries <3) {
+ $tries ++;
+ sleep 1;
+ $gotlock = &newput('file_permissions',$lockhash,$domain,$user);
+ }
+ if ($gotlock eq 'ok') {
+ my %curr_permissions = &dump('file_permissions',$domain,$user,$file_name);
+ my ($tmp)=keys(%curr_permissions);
+ if ($tmp=~/^error:/) { undef(%curr_permissions); }
+ if (exists($curr_permissions{$file_name."\0".'accesscontrol'})) {
+ my $curr_controls = $curr_permissions{$file_name."\0".'accesscontrol'};
+ if (ref($curr_controls) eq 'HASH') {
+ foreach my $control_item (keys(%{$curr_controls})) {
+ my ($itemnum) = ($control_item =~ /^([^:]+):/);
+ if (defined($todelete{$itemnum})) {
+ push(@deletions,$file_name."\0".$control_item);
+ } else {
+ if (defined($changed_items{$itemnum})) {
+ $new_control{$changed_items{$itemnum}} = $now;
+ push(@deletions,$file_name."\0".$control_item);
+ $new_values{$file_name."\0".$changed_items{$itemnum}} = $$changes{'update'}{$changed_items{$itemnum}};
+ } else {
+ $new_control{$control_item} = $$curr_controls{$control_item};
+ }
+ }
+ }
+ }
+ }
+ $deloutcome = &del('file_permissions',\@deletions,$domain,$user);
+ $new_values{$file_name."\0".'accesscontrol'} = \%new_control;
+ $outcome = &put('file_permissions',\%new_values,$domain,$user);
+ # remove lock
+ my @del_lock = ($file_name."\0".'locked_access_records');
+ my $dellockoutcome = &del('file_permissions',\@del_lock,$domain,$user);
+ } else {
+ $outcome = "error: could not obtain lockfile\n";
+ }
+ return ($outcome,$deloutcome,\%new_values,\%translation);
+}
+
+#------------------------------------------------------Get Marked as Read Only
+
+sub get_marked_as_readonly {
+ my ($domain,$user,$what,$group) = @_;
+ my $current_permissions = &get_portfile_permissions($domain,$user);
my @readonly_files;
my $cmp1=$what;
if (ref($what)) { $cmp1=join('',@{$what}) };
- while (my ($file_name,$value) = each(%current_permissions)) {
+ while (my ($file_name,$value) = each(%{$current_permissions})) {
+ if (defined($group)) {
+ if ($file_name !~ m-^\Q$group\E/-) {
+ next;
+ }
+ }
if (ref($value) eq "ARRAY"){
foreach my $stored_what (@{$value}) {
my $cmp2=$stored_what;
- if (ref($stored_what)) { $cmp2=join('',@{$stored_what}) };
+ if (ref($stored_what) eq 'ARRAY') {
+ $cmp2=join('',@{$stored_what});
+ }
if ($cmp1 eq $cmp2) {
push(@readonly_files, $file_name);
+ last;
} elsif (!defined($what)) {
push(@readonly_files, $file_name);
+ last;
}
}
- }
+ }
}
return @readonly_files;
}
#-----------------------------------------------------------Get Marked as Read Only Hash
sub get_marked_as_readonly_hash {
- my ($domain,$user,$what) = @_;
- my %current_permissions = &dump('file_permissions',$domain,$user);
- my ($tmp)=keys(%current_permissions);
- if ($tmp=~/^error:/) { undef(%current_permissions); }
-
+ my ($current_permissions,$group,$what) = @_;
my %readonly_files;
- while (my ($file_name,$value) = each(%current_permissions)) {
+ while (my ($file_name,$value) = each(%{$current_permissions})) {
+ if (defined($group)) {
+ if ($file_name !~ m-^\Q$group\E/-) {
+ next;
+ }
+ }
if (ref($value) eq "ARRAY"){
foreach my $stored_what (@{$value}) {
- if ($stored_what eq $what) {
- $readonly_files{$file_name} = 'locked';
- } elsif (!defined($what)) {
- $readonly_files{$file_name} = 'locked';
- }
+ if (ref($stored_what) eq 'ARRAY') {
+ foreach my $lock_descriptor(@{$stored_what}) {
+ if ($lock_descriptor eq 'graded') {
+ $readonly_files{$file_name} = 'graded';
+ } elsif ($lock_descriptor eq 'handback') {
+ $readonly_files{$file_name} = 'handback';
+ } else {
+ if (!exists($readonly_files{$file_name})) {
+ $readonly_files{$file_name} = 'locked';
+ }
+ }
+ }
+ }
}
}
}
@@ -4364,24 +5142,28 @@ sub get_marked_as_readonly_hash {
sub unmark_as_readonly {
# unmarks $file_name (if $file_name is defined), or all files locked by $what
# for portfolio submissions, $what contains [$symb,$crsid]
- my ($domain,$user,$what,$file_name) = @_;
+ my ($domain,$user,$what,$file_name,$group) = @_;
+ $file_name = &declutter_portfile($file_name);
my $symb_crs = $what;
if (ref($what)) { $symb_crs=join('',@$what); }
- my %current_permissions = &dump('file_permissions',$domain,$user);
+ my %current_permissions = &dump('file_permissions',$domain,$user,$group);
my ($tmp)=keys(%current_permissions);
if ($tmp=~/^error:/) { undef(%current_permissions); }
- my @readonly_files = &get_marked_as_readonly($domain,$user,$what);
+ my @readonly_files = &get_marked_as_readonly($domain,$user,$what,$group);
foreach my $file (@readonly_files) {
- if (defined($file_name) && ($file_name ne $file)) { next; }
+ my $clean_file = &declutter_portfile($file);
+ if (defined($file_name) && ($file_name ne $clean_file)) { next; }
my $current_locks = $current_permissions{$file};
my @new_locks;
my @del_keys;
if (ref($current_locks) eq "ARRAY"){
foreach my $locker (@{$current_locks}) {
my $compare=$locker;
- if (ref($locker)) { $compare=join('',@{$locker}) };
- if ($compare ne $symb_crs) {
- push(@new_locks, $locker);
+ if (ref($locker) eq 'ARRAY') {
+ $compare=join('',@{$locker});
+ if ($compare ne $symb_crs) {
+ push(@new_locks, $locker);
+ }
}
}
if (scalar(@new_locks) > 0) {
@@ -4519,13 +5301,64 @@ sub GetFileTimestamp {
}
}
+sub stat_file {
+ my ($uri) = @_;
+ $uri = &clutter_with_no_wrapper($uri);
+
+ my ($udom,$uname,$file,$dir);
+ if ($uri =~ m-^/(uploaded|editupload)/-) {
+ ($udom,$uname,$file) =
+ ($uri =~ m-/(?:uploaded|editupload)/?([^/]*)/?([^/]*)/?(.*)-);
+ $file = 'userfiles/'.$file;
+ $dir = &propath($udom,$uname);
+ }
+ if ($uri =~ m-^/res/-) {
+ ($udom,$uname) =
+ ($uri =~ m-/(?:res)/?([^/]*)/?([^/]*)/-);
+ $file = $uri;
+ }
+
+ if (!$udom || !$uname || !$file) {
+ # unable to handle the uri
+ return ();
+ }
+
+ my ($result) = &dirlist($file,$udom,$uname,$dir);
+ my @stats = split('&', $result);
+
+ if($stats[0] ne 'empty' && $stats[0] ne 'no_such_dir') {
+ shift(@stats); #filename is first
+ return @stats;
+ }
+ return ();
+}
+
# -------------------------------------------------------- Value of a Condition
+# gets the value of a specific preevaluated condition
+# stored in the string $env{user.state.}
+# or looks up a condition reference in the bighash and if if hasn't
+# already been evaluated recurses into docondval to get the value of
+# the condition, then memoizing it to
+# $env{user.state..}
sub directcondval {
my $number=shift;
if (!defined($env{'user.state.'.$env{'request.course.id'}})) {
&Apache::lonuserstate::evalstate();
}
+ if (exists($env{'user.state.'.$env{'request.course.id'}.".$number"})) {
+ return $env{'user.state.'.$env{'request.course.id'}.".$number"};
+ } elsif ($number =~ /^_/) {
+ my $sub_condition;
+ if (tie(my %bighash,'GDBM_File',$env{'request.course.fn'}.'.db',
+ &GDBM_READER(),0640)) {
+ $sub_condition=$bighash{'conditions'.$number};
+ untie(%bighash);
+ }
+ my $value = &docondval($sub_condition);
+ &appenv('user.state.'.$env{'request.course.id'}.".$number" => $value);
+ return $value;
+ }
if ($env{'user.state.'.$env{'request.course.id'}}) {
return substr($env{'user.state.'.$env{'request.course.id'}},$number,1);
} else {
@@ -4533,43 +5366,49 @@ sub directcondval {
}
}
+# get the collection of conditions for this resource
sub condval {
my $condidx=shift;
- my $result=0;
my $allpathcond='';
- foreach (split(/\|/,$condidx)) {
- if (defined($env{'acc.cond.'.$env{'request.course.id'}.'.'.$_})) {
- $allpathcond.=
- '('.$env{'acc.cond.'.$env{'request.course.id'}.'.'.$_}.')|';
- }
+ foreach my $cond (split(/\|/,$condidx)) {
+ if (defined($env{'acc.cond.'.$env{'request.course.id'}.'.'.$cond})) {
+ $allpathcond.=
+ '('.$env{'acc.cond.'.$env{'request.course.id'}.'.'.$cond}.')|';
+ }
}
$allpathcond=~s/\|$//;
- if ($env{'request.course.id'}) {
- if ($allpathcond) {
- my $operand='|';
- my @stack;
- foreach ($allpathcond=~/(\d+|\(|\)|\&|\|)/g) {
- if ($_ eq '(') {
- push @stack,($operand,$result)
- } elsif ($_ eq ')') {
- my $before=pop @stack;
- if (pop @stack eq '&') {
- $result=$result>$before?$before:$result;
- } else {
- $result=$result>$before?$result:$before;
- }
- } elsif (($_ eq '&') || ($_ eq '|')) {
- $operand=$_;
- } else {
- my $new=directcondval($_);
- if ($operand eq '&') {
- $result=$result>$new?$new:$result;
- } else {
- $result=$result>$new?$result:$new;
- }
- }
- }
- }
+ return &docondval($allpathcond);
+}
+
+#evaluates an expression of conditions
+sub docondval {
+ my ($allpathcond) = @_;
+ my $result=0;
+ if ($env{'request.course.id'}
+ && defined($allpathcond)) {
+ my $operand='|';
+ my @stack;
+ foreach my $chunk ($allpathcond=~/(\d+|_\d+\.\d+|\(|\)|\&|\|)/g) {
+ if ($chunk eq '(') {
+ push @stack,($operand,$result);
+ } elsif ($chunk eq ')') {
+ my $before=pop @stack;
+ if (pop @stack eq '&') {
+ $result=$result>$before?$before:$result;
+ } else {
+ $result=$result>$before?$result:$before;
+ }
+ } elsif (($chunk eq '&') || ($chunk eq '|')) {
+ $operand=$chunk;
+ } else {
+ my $new=directcondval($chunk);
+ if ($operand eq '&') {
+ $result=$result>$new?$new:$result;
+ } else {
+ $result=$result>$new?$result:$new;
+ }
+ }
+ }
}
return $result;
}
@@ -4582,6 +5421,7 @@ sub devalidatecourseresdata {
&devalidate_cache_new('courseres',$hashid);
}
+
# --------------------------------------------------- Course Resourcedata Query
sub get_courseresdata {
@@ -4686,8 +5526,8 @@ sub EXT_cache_set {
# --------------------------------------------------------- Value of a Variable
sub EXT {
- my ($varname,$symbparm,$udom,$uname,$usection,$recurse)=@_;
+ my ($varname,$symbparm,$udom,$uname,$usection,$recurse)=@_;
unless ($varname) { return ''; }
#get real user name/domain, courseid and symb
my $courseid;
@@ -4696,8 +5536,7 @@ sub EXT {
$symbparm=&get_symb_from_alias($symbparm);
}
if (!($uname && $udom)) {
- (my $cursymb,$courseid,$udom,$uname,$publicuser)=
- &Apache::lonxml::whichuser($symbparm);
+ (my $cursymb,$courseid,$udom,$uname,$publicuser)= &whichuser($symbparm);
if (!$symbparm) { $symbparm=$cursymb; }
} else {
$courseid=$env{'request.course.id'};
@@ -4720,8 +5559,14 @@ sub EXT {
if ( (defined($Apache::lonhomework::parsing_a_problem)
|| defined($Apache::lonhomework::parsing_a_task))
&&
- ($symbparm eq &symbread()) ) {
- return $Apache::lonhomework::history{$qualifierrest};
+ ($symbparm eq &symbread()) ) {
+ # if we are in the middle of processing the resource the
+ # get the value we are planning on committing
+ if (defined($Apache::lonhomework::results{$qualifierrest})) {
+ return $Apache::lonhomework::results{$qualifierrest};
+ } else {
+ return $Apache::lonhomework::history{$qualifierrest};
+ }
} else {
my %restored;
if ($publicuser || $env{'request.state'} eq 'construct') {
@@ -4801,10 +5646,21 @@ sub EXT {
return $env{'course.'.$courseid.'.'.$spacequalifierrest};
} elsif ($realm eq 'resource') {
- my $section;
if (defined($courseid) && $courseid eq $env{'request.course.id'}) {
if (!$symbparm) { $symbparm=&symbread(); }
}
+
+ if ($space eq 'title') {
+ if (!$symbparm) { $symbparm = $env{'request.filename'}; }
+ return &gettitle($symbparm);
+ }
+
+ if ($space eq 'map') {
+ my ($map) = &decode_symb($symbparm);
+ return &symbread($map);
+ }
+
+ my ($section, $group, @groups);
my ($courselevelm,$courselevel);
if ($symbparm && defined($courseid) &&
$courseid eq $env{'request.course.id'}) {
@@ -4813,7 +5669,7 @@ sub EXT {
# ----------------------------------------------------- Cascading lookup scheme
my $symbp=$symbparm;
- my $mapp=(&decode_symb($symbp))[0];
+ my $mapp=&deversion((&decode_symb($symbp))[0]);
my $symbparm=$symbp.'.'.$spacequalifierrest;
my $mapparm=$mapp.'___(all).'.$spacequalifierrest;
@@ -4821,12 +5677,15 @@ sub EXT {
if (($env{'user.name'} eq $uname) &&
($env{'user.domain'} eq $udom)) {
$section=$env{'request.course.sec'};
+ @groups = split(/:/,$env{'request.course.groups'});
+ @groups=&sort_course_groups($courseid,@groups);
} else {
if (! defined($usection)) {
$section=&getsection($udom,$uname,$courseid);
} else {
$section = $usection;
}
+ @groups = &get_users_groups($udom,$uname,$courseid);
}
my $seclevel=$courseid.'.['.$section.'].'.$spacequalifierrest;
@@ -4842,12 +5701,17 @@ sub EXT {
my $userreply=&resdata($uname,$udom,'user',
($courselevelr,$courselevelm,
$courselevel));
-
if (defined($userreply)) { return $userreply; }
# ------------------------------------------------ second, check some of course
+ my $coursereply;
+ if (@groups > 0) {
+ $coursereply = &check_group_parms($courseid,\@groups,$symbparm,
+ $mapparm,$spacequalifierrest);
+ if (defined($coursereply)) { return $coursereply; }
+ }
- my $coursereply=&resdata($env{'course.'.$courseid.'.num'},
+ $coursereply=&resdata($env{'course.'.$courseid.'.num'},
$env{'course.'.$courseid.'.domain'},
'course',
($seclevelr,$seclevelm,$seclevel,
@@ -4909,6 +5773,9 @@ sub EXT {
if (($uname eq $env{'user.name'})&&($udom eq $env{'user.domain'})) {
return $env{'environment.'.$spacequalifierrest};
} else {
+ if ($uname eq 'anonymous' && $udom eq '') {
+ return '';
+ }
my %returnhash=&userenvironment($udom,$uname,
$spacequalifierrest);
return $returnhash{$spacequalifierrest};
@@ -4918,16 +5785,64 @@ sub EXT {
if ($space eq 'time') {
return time;
}
+ } elsif ($realm eq 'server') {
+# ----------------------------------------------------------------- system.time
+ if ($space eq 'name') {
+ return $ENV{'SERVER_NAME'};
+ }
}
return '';
}
+sub check_group_parms {
+ my ($courseid,$groups,$symbparm,$mapparm,$what) = @_;
+ my @groupitems = ();
+ my $resultitem;
+ my @levels = ($symbparm,$mapparm,$what);
+ foreach my $group (@{$groups}) {
+ foreach my $level (@levels) {
+ my $item = $courseid.'.['.$group.'].'.$level;
+ push(@groupitems,$item);
+ }
+ }
+ my $coursereply = &resdata($env{'course.'.$courseid.'.num'},
+ $env{'course.'.$courseid.'.domain'},
+ 'course',@groupitems);
+ return $coursereply;
+}
+
+sub sort_course_groups { # Sort groups based on defined rankings. Default is sort().
+ my ($courseid,@groups) = @_;
+ @groups = sort(@groups);
+ return @groups;
+}
+
sub packages_tab_default {
my ($uri,$varname)=@_;
my (undef,$part,$name)=split(/\./,$varname);
- my $packages=&metadata($uri,'packages');
- foreach my $package (split(/,/,$packages)) {
+
+ my (@extension,@specifics,$do_default);
+ foreach my $package (split(/,/,&metadata($uri,'packages'))) {
my ($pack_type,$pack_part)=split(/_/,$package,2);
+ if ($pack_type eq 'default') {
+ $do_default=1;
+ } elsif ($pack_type eq 'extension') {
+ push(@extension,[$package,$pack_type,$pack_part]);
+ } else {
+ push(@specifics,[$package,$pack_type,$pack_part]);
+ }
+ }
+ # first look for a package that matches the requested part id
+ foreach my $package (@specifics) {
+ my (undef,$pack_type,$pack_part)=@{$package};
+ next if ($pack_part ne $part);
+ if (defined($packagetab{"$pack_type&$name&default"})) {
+ return $packagetab{"$pack_type&$name&default"};
+ }
+ }
+ # look for any possible matching non extension_ package
+ foreach my $package (@specifics) {
+ my (undef,$pack_type,$pack_part)=@{$package};
if (defined($packagetab{"$pack_type&$name&default"})) {
return $packagetab{"$pack_type&$name&default"};
}
@@ -4936,6 +5851,20 @@ sub packages_tab_default {
return $packagetab{$pack_type."_".$pack_part."&$name&default"};
}
}
+ # look for any posible extension_ match
+ foreach my $package (@extension) {
+ my ($package,$pack_type)=@{$package};
+ if (defined($packagetab{"$pack_type&$name&default"})) {
+ return $packagetab{"$pack_type&$name&default"};
+ }
+ if (defined($packagetab{$package."&$name&default"})) {
+ return $packagetab{$package."&$name&default"};
+ }
+ }
+ # look for a global default setting
+ if ($do_default && defined($packagetab{"default&$name&default"})) {
+ return $packagetab{"default&$name&default"};
+ }
return undef;
}
@@ -4997,7 +5926,7 @@ sub metadata {
my %metathesekeys=();
unless ($filename=~/\.meta$/) { $filename.='.meta'; }
my $metastring;
- if ($uri !~ m -^(uploaded|editupload)/-) {
+ if ($uri !~ m -^(editupload)/-) {
my $file=&filelocation('',&clutter($filename));
#push(@{$metaentry{$uri.'.file'}},$file);
$metastring=&getfile($file);
@@ -5021,16 +5950,16 @@ sub metadata {
} else {
$metaentry{':packages'}=$package.$keyroot;
}
- foreach (sort keys %packagetab) {
+ foreach my $pack_entry (keys(%packagetab)) {
my $part=$keyroot;
$part=~s/^\_//;
- if ($_=~/^\Q$package\E\&/ ||
- $_=~/^\Q$package\E_0\&/) {
- my ($pack,$name,$subp)=split(/\&/,$_);
+ if ($pack_entry=~/^\Q$package\E\&/ ||
+ $pack_entry=~/^\Q$package\E_0\&/) {
+ my ($pack,$name,$subp)=split(/\&/,$pack_entry);
# ignore package.tab specified default values
# here &package_tab_default() will fetch those
if ($subp eq 'default') { next; }
- my $value=$packagetab{$_};
+ my $value=$packagetab{$pack_entry};
my $unikey;
if ($pack =~ /_0$/) {
$unikey='parameter_0_'.$name;
@@ -5078,11 +6007,12 @@ sub metadata {
my $dir=$filename;
$dir=~s|[^/]*$||;
$location=&filelocation($dir,$location);
- foreach (sort(split(/\,/,&metadata($uri,'keys',
- $location,$unikey,
- $depthcount+1)))) {
- $metaentry{':'.$_}=$metaentry{':'.$_};
- $metathesekeys{$_}=1;
+ my $metadata =
+ &metadata($uri,'keys', $location,$unikey,
+ $depthcount+1);
+ foreach my $meta (split(',',$metadata)) {
+ $metaentry{':'.$meta}=$metaentry{':'.$meta};
+ $metathesekeys{$meta}=1;
}
}
} else {
@@ -5091,8 +6021,9 @@ sub metadata {
$unikey.='_'.$token->[2]->{'name'};
}
$metathesekeys{$unikey}=1;
- foreach (@{$token->[3]}) {
- $metaentry{':'.$unikey.'.'.$_}=$token->[2]->{$_};
+ foreach my $param (@{$token->[3]}) {
+ $metaentry{':'.$unikey.'.'.$param} =
+ $token->[2]->{$param};
}
my $internaltext=&HTML::Entities::decode($parser->get_text('/'.$entry));
my $default=$metaentry{':'.$unikey.'.default'};
@@ -5113,14 +6044,14 @@ sub metadata {
}
}
my ($extension) = ($uri =~ /\.(\w+)$/);
- foreach my $key (sort(keys(%packagetab))) {
+ foreach my $key (keys(%packagetab)) {
#no specific packages #how's our extension
if ($key!~/^extension_\Q$extension\E&/) { next; }
&metadata_create_package_def($uri,$key,'extension_'.$extension,
\%metathesekeys);
}
if (!exists($metaentry{':packages'})) {
- foreach my $key (sort(keys(%packagetab))) {
+ foreach my $key (keys(%packagetab)) {
#no specific packages well let's get default then
if ($key!~/^default&/) { next; }
&metadata_create_package_def($uri,$key,'default',
@@ -5138,18 +6069,25 @@ sub metadata {
my $dir=$filename;
$dir=~s|[^/]*$||;
$location=&filelocation($dir,$location);
- foreach (sort(split(/\,/,&metadata($uri,'keys',
- $location,'_rights',
- $depthcount+1)))) {
- #$metaentry{':'.$_}=$metacache{$uri}->{':'.$_};
- $metathesekeys{$_}=1;
+ my $rights_metadata =
+ &metadata($uri,'keys',$location,'_rights',
+ $depthcount+1);
+ foreach my $rights (split(',',$rights_metadata)) {
+ #$metaentry{':'.$rights}=$metacache{$uri}->{':'.$rights};
+ $metathesekeys{$rights}=1;
}
}
}
- $metaentry{':keys'}=join(',',keys %metathesekeys);
+ # uniqifiy package listing
+ my %seen;
+ my @uniq_packages =
+ grep { ! $seen{$_} ++ } (split(',',$metaentry{':packages'}));
+ $metaentry{':packages'} = join(',',@uniq_packages);
+
+ $metaentry{':keys'} = join(',',keys(%metathesekeys));
&metadata_generate_part0(\%metathesekeys,\%metaentry,$uri);
$metaentry{':allpossiblekeys'}=join(',',keys %metathesekeys);
- &do_cache_new('meta',$uri,\%metaentry,60*60*24);
+ &do_cache_new('meta',$uri,\%metaentry,60*60);
# this is the end of "was not already recently cached
}
return $metaentry{':'.$what};
@@ -5182,7 +6120,7 @@ sub metadata_create_package_def {
sub metadata_generate_part0 {
my ($metadata,$metacache,$uri) = @_;
my %allnames;
- foreach my $metakey (sort keys %$metadata) {
+ foreach my $metakey (keys(%$metadata)) {
if ($metakey=~/^parameter\_(.*)/) {
my $part=$$metacache{':'.$metakey.'.part'};
my $name=$$metacache{':'.$metakey.'.name'};
@@ -5207,6 +6145,17 @@ sub metadata_generate_part0 {
}
}
+# ------------------------------------------------------ Devalidate title cache
+
+sub devalidate_title_cache {
+ my ($url)=@_;
+ if (!$env{'request.course.id'}) { return; }
+ my $symb=&symbread($url);
+ if (!$symb) { return; }
+ my $key=$env{'request.course.id'}."\0".$symb;
+ &devalidate_cache_new('title',$key);
+}
+
# ------------------------------------------------- Get the title of a resource
sub gettitle {
@@ -5241,14 +6190,21 @@ sub gettitle {
sub get_slot {
my ($which,$cnum,$cdom)=@_;
if (!$cnum || !$cdom) {
- (undef,my $courseid)=&Apache::lonxml::whichuser();
+ (undef,my $courseid)=&whichuser();
$cdom=$env{'course.'.$courseid.'.domain'};
$cnum=$env{'course.'.$courseid.'.num'};
}
- my %slotinfo=&get('slots',[$which],$cdom,$cnum);
- &Apache::lonhomework::showhash(%slotinfo);
- my ($tmp)=keys(%slotinfo);
- if ($tmp=~/^error:/) { return (); }
+ my $key=join("\0",'slots',$cdom,$cnum,$which);
+ my %slotinfo;
+ if (exists($remembered{$key})) {
+ $slotinfo{$which} = $remembered{$key};
+ } else {
+ %slotinfo=&get('slots',[$which],$cdom,$cnum);
+ &Apache::lonhomework::showhash(%slotinfo);
+ my ($tmp)=keys(%slotinfo);
+ if ($tmp=~/^error:/) { return (); }
+ $remembered{$key} = $slotinfo{$which};
+ }
if (ref($slotinfo{$which}) eq 'HASH') {
return %{$slotinfo{$which}};
}
@@ -5263,9 +6219,12 @@ sub symblist {
if (($env{'request.course.fn'}) && (%newhash)) {
if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db',
&GDBM_WRCREAT(),0640)) {
- foreach (keys %newhash) {
- $hash{declutter($_)}=&encode_symb($mapname,$newhash{$_}->[1],
- $newhash{$_}->[0]);
+ foreach my $url (keys %newhash) {
+ next if ($url eq 'last_known'
+ && $env{'form.no_update_last_known'});
+ $hash{declutter($url)}=&encode_symb($mapname,
+ $newhash{$url}->[1],
+ $newhash{$url}->[0]);
}
if (untie(%hash)) {
return 'ok';
@@ -5280,8 +6239,6 @@ sub symblist {
sub symbverify {
my ($symb,$thisurl)=@_;
my $thisfn=$thisurl;
-# wrapper not part of symbs
- $thisfn=~s/^\/adm\/wrapper//;
$thisfn=&declutter($thisfn);
# direct jump to resource in page or to a sequence - will construct own symbs
if ($thisfn=~/\.(page|sequence)$/) { return 1; }
@@ -5336,6 +6293,7 @@ sub symbclean {
# remove wrapper
$symb=~s/(\_\_\_\d+\_\_\_)adm\/wrapper\/(res\/)*/$1/;
+ $symb=~s/(\_\_\_\d+\_\_\_)adm\/coursedocs\/showdoc\/(res\/)*/$1/;
return $symb;
}
@@ -5412,6 +6370,9 @@ sub symbread {
if ( ($thisfn =~ m/^(uploaded|editupload)\//) && ($thisfn !~ m/\.(page|sequence)$/) ) {
$targetfn = 'adm/wrapper/'.$thisfn;
}
+ if ($targetfn =~ m|^adm/wrapper/(ext/.*)|) {
+ $targetfn=$1;
+ }
if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db',
&GDBM_READER(),0640)) {
$syval=$hash{$targetfn};
@@ -5560,7 +6521,7 @@ sub latest_rnd_algorithm_id {
sub get_rand_alg {
my ($courseid)=@_;
- if (!$courseid) { $courseid=(&Apache::lonxml::whichuser())[1]; }
+ if (!$courseid) { $courseid=(&whichuser())[1]; }
if ($courseid) {
return $env{"course.$courseid.rndseed"};
}
@@ -5586,7 +6547,7 @@ sub getCODE {
sub rndseed {
my ($symb,$courseid,$domain,$username)=@_;
- my ($wsymb,$wcourseid,$wdomain,$wusername)=&Apache::lonxml::whichuser();
+ my ($wsymb,$wcourseid,$wdomain,$wusername)=&whichuser();
if (!$symb) {
unless ($symb=$wsymb) { return time; }
}
@@ -5627,8 +6588,8 @@ sub rndseed_32bit {
my $domainseed=unpack("%32C*",$domain) << 7;
my $courseseed=unpack("%32C*",$courseid);
my $num=$symbseed+$nameseed+$domainseed+$courseseed+$namechck+$symbchck;
- #&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck");
- #&Apache::lonxml::debug("rndseed :$num:$symb");
+ #&logthis("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck");
+ #&logthis("rndseed :$num:$symb");
if ($_64bit) { $num=(($num<<32)>>32); }
return $num;
}
@@ -5648,8 +6609,8 @@ sub rndseed_64bit {
my $num1=$symbchck+$symbseed+$namechck;
my $num2=$nameseed+$domainseed+$courseseed;
- #&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck");
- #&Apache::lonxml::debug("rndseed :$num:$symb");
+ #&logthis("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck");
+ #&logthis("rndseed :$num:$symb");
if ($_64bit) { $num1=(($num1<<32)>>32); $num2=(($num2<<32)>>32); }
if ($_64bit) { $num1=(($num1<<32)>>32); $num2=(($num2<<32)>>32); }
return "$num1,$num2";
@@ -5672,8 +6633,8 @@ sub rndseed_64bit2 {
my $num1=$symbchck+$symbseed+$namechck;
my $num2=$nameseed+$domainseed+$courseseed;
- #&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck");
- #&Apache::lonxml::debug("rndseed :$num:$symb");
+ #&logthis("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck");
+ #&logthis("rndseed :$num:$symb");
return "$num1,$num2";
}
}
@@ -5694,8 +6655,8 @@ sub rndseed_64bit3 {
my $num1=$symbchck+$symbseed+$namechck;
my $num2=$nameseed+$domainseed+$courseseed;
- #&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck");
- #&Apache::lonxml::debug("rndseed :$num1:$num2:$_64bit");
+ #&logthis("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck");
+ #&logthis("rndseed :$num1:$num2:$_64bit");
if ($_64bit) { $num1=(($num1<<32)>>32); $num2=(($num2<<32)>>32); }
return "$num1:$num2";
@@ -5718,8 +6679,8 @@ sub rndseed_64bit4 {
my $num1=$symbchck+$symbseed+$namechck;
my $num2=$nameseed+$domainseed+$courseseed;
- #&Apache::lonxml::debug("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck");
- #&Apache::lonxml::debug("rndseed :$num1:$num2:$_64bit");
+ #&logthis("$symbseed:$nameseed;$domainseed|$courseseed;$namechck:$symbchck");
+ #&logthis("rndseed :$num1:$num2:$_64bit");
if ($_64bit) { $num1=(($num1<<32)>>32); $num2=(($num2<<32)>>32); }
return "$num1:$num2";
@@ -5743,8 +6704,8 @@ sub rndseed_CODE_64bit {
my $courseseed=unpack("%32S*",$courseid.' ');
my $num1=$symbseed+$CODEchck;
my $num2=$CODEseed+$courseseed+$symbchck;
- #&Apache::lonxml::debug("$symbseed:$CODEchck|$CODEseed:$courseseed:$symbchck");
- #&Apache::lonxml::debug("rndseed :$num1:$num2:$symb");
+ #&logthis("$symbseed:$CODEchck|$CODEseed:$courseseed:$symbchck");
+ #&logthis("rndseed :$num1:$num2:$symb");
if ($_64bit) { $num1=(($num1<<32)>>32); }
if ($_64bit) { $num2=(($num2<<32)>>32); }
return "$num1:$num2";
@@ -5762,8 +6723,8 @@ sub rndseed_CODE_64bit4 {
my $courseseed=unpack("%32S*",$courseid.' ');
my $num1=$symbseed+$CODEchck;
my $num2=$CODEseed+$courseseed+$symbchck;
- #&Apache::lonxml::debug("$symbseed:$CODEchck|$CODEseed:$courseseed:$symbchck");
- #&Apache::lonxml::debug("rndseed :$num1:$num2:$symb");
+ #&logthis("$symbseed:$CODEchck|$CODEseed:$courseseed:$symbchck");
+ #&logthis("rndseed :$num1:$num2:$symb");
if ($_64bit) { $num1=(($num1<<32)>>32); }
if ($_64bit) { $num2=(($num2<<32)>>32); }
return "$num1:$num2";
@@ -5824,8 +6785,7 @@ sub ireceipt {
my $return =&recprefix($fucourseid).'-';
if ($env{"course.$fucourseid.receiptalg"} eq 'receipt2' ||
$env{'request.state'} eq 'construct') {
- &Apache::lonxml::debug("doing receipt2 using parts $cpart, uname $cuname and udom $cudom gets ".($cpart%$cuname).
- " and ".($cpart%$cudom));
+ #&logthis("doing receipt2 using parts $cpart, uname $cuname and udom $cudom gets ".($cpart%$cuname)." and ".($cpart%$cudom));
$return.= ($cunique%$cuname+
$cunique%$cudom+
@@ -5848,10 +6808,48 @@ sub ireceipt {
sub receipt {
my ($part)=@_;
- my ($symb,$courseid,$domain,$name) = &Apache::lonxml::whichuser();
+ my ($symb,$courseid,$domain,$name) = &whichuser();
return &ireceipt($name,$domain,$courseid,$symb,$part);
}
+sub whichuser {
+ my ($passedsymb)=@_;
+ my ($symb,$courseid,$domain,$name,$publicuser);
+ if (defined($env{'form.grade_symb'})) {
+ my ($tmp_courseid)=&get_env_multiple('form.grade_courseid');
+ my $allowed=&allowed('vgr',$tmp_courseid);
+ if (!$allowed &&
+ exists($env{'request.course.sec'}) &&
+ $env{'request.course.sec'} !~ /^\s*$/) {
+ $allowed=&allowed('vgr',$tmp_courseid.
+ '/'.$env{'request.course.sec'});
+ }
+ if ($allowed) {
+ ($symb)=&get_env_multiple('form.grade_symb');
+ $courseid=$tmp_courseid;
+ ($domain)=&get_env_multiple('form.grade_domain');
+ ($name)=&get_env_multiple('form.grade_username');
+ return ($symb,$courseid,$domain,$name,$publicuser);
+ }
+ }
+ if (!$passedsymb) {
+ $symb=&symbread();
+ } else {
+ $symb=$passedsymb;
+ }
+ $courseid=$env{'request.course.id'};
+ $domain=$env{'user.domain'};
+ $name=$env{'user.name'};
+ if ($name eq 'public' && $domain eq 'public') {
+ if (!defined($env{'form.username'})) {
+ $env{'form.username'}.=time.rand(10000000);
+ }
+ $name.=$env{'form.username'};
+ }
+ return ($symb,$courseid,$domain,$name,$publicuser);
+
+}
+
# ------------------------------------------------------------ Serves up a file
# returns either the contents of the file or
# -1 if the file doesn't exist
@@ -5983,6 +6981,11 @@ sub filelocation {
my ($dir,$file) = @_;
my $location;
$file=~ s/^\s*(\S+)\s*$/$1/; ## strip off leading and trailing spaces
+
+ if ($file =~ m-^/adm/-) {
+ $file=~s-^/adm/wrapper/-/-;
+ $file=~s-^/adm/coursedocs/showdoc/-/-;
+ }
if ($file=~m:^/~:) { # is a contruction space reference
$location = $file;
$location =~ s:/~(.*?)/(.*):/home/$1/public_html/$2:;
@@ -5997,7 +7000,7 @@ sub filelocation {
my @ids=¤t_machine_ids();
foreach my $id (@ids) { if ($id eq $home) { $is_me=1; } }
if ($is_me) {
- $location=&Apache::loncommon::propath($udom,$uname).
+ $location=&propath($udom,$uname).
'/userfiles/'.$filename;
} else {
$location=$Apache::lonnet::perlvar{'lonDocRoot'}.'/userfiles/'.
@@ -6022,6 +7025,9 @@ sub hreflocation {
my ($dir,$file)=@_;
unless (($file=~m-^http://-i) || ($file=~m-^/-)) {
$file=filelocation($dir,$file);
+ } elsif ($file=~m-^/adm/-) {
+ $file=~s-^/adm/wrapper/-/-;
+ $file=~s-^/adm/coursedocs/showdoc/-/-;
}
if ($file=~m-^\Q$perlvar{'lonDocRoot'}\E-) {
$file=~s-^\Q$perlvar{'lonDocRoot'}\E--;
@@ -6065,6 +7071,8 @@ sub declutter {
if ($thisfn=~m|^/enc/|) { $thisfn=&Apache::lonenc::unencrypted($thisfn); }
$thisfn=~s/^\Q$perlvar{'lonDocRoot'}\E//;
$thisfn=~s/^\///;
+ $thisfn=~s|^adm/wrapper/||;
+ $thisfn=~s|^adm/coursedocs/showdoc/||;
$thisfn=~s/^res\///;
$thisfn=~s/\?.+$//;
return $thisfn;
@@ -6077,9 +7085,42 @@ sub clutter {
unless ($thisfn=~/^\/(uploaded|editupload|adm|userfiles|ext|raw|priv|public)\//) {
$thisfn='/res'.$thisfn;
}
+ if ($thisfn !~m|/adm|) {
+ if ($thisfn =~ m|/ext/|) {
+ $thisfn='/adm/wrapper'.$thisfn;
+ } else {
+ my ($ext) = ($thisfn =~ /\.(\w+)$/);
+ my $embstyle=&Apache::loncommon::fileembstyle($ext);
+ if ($embstyle eq 'ssi'
+ || ($embstyle eq 'hdn')
+ || ($embstyle eq 'rat')
+ || ($embstyle eq 'prv')
+ || ($embstyle eq 'ign')) {
+ #do nothing with these
+ } elsif (($embstyle eq 'img')
+ || ($embstyle eq 'emb')
+ || ($embstyle eq 'wrp')) {
+ $thisfn='/adm/wrapper'.$thisfn;
+ } elsif ($embstyle eq 'unk'
+ && $thisfn!~/\.(sequence|page)$/) {
+ $thisfn='/adm/coursedocs/showdoc'.$thisfn;
+ } else {
+# &logthis("Got a blank emb style");
+ }
+ }
+ }
return $thisfn;
}
+sub clutter_with_no_wrapper {
+ my $uri = &clutter(shift);
+ if ($uri =~ m-^/adm/-) {
+ $uri =~ s-^/adm/wrapper/-/-;
+ $uri =~ s-^/adm/coursedocs/showdoc/-/-;
+ }
+ return $uri;
+}
+
sub freeze_escape {
my ($value)=@_;
if (ref($value)) {
@@ -6089,21 +7130,6 @@ sub freeze_escape {
return &escape($value);
}
-# -------------------------------------------------------- Escape Special Chars
-
-sub escape {
- my $str=shift;
- $str =~ s/(\W)/"%".unpack('H2',$1)/eg;
- return $str;
-}
-
-# ----------------------------------------------------- Un-Escape Special Chars
-
-sub unescape {
- my $str=shift;
- $str =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
- return $str;
-}
sub thaw_unescape {
my ($value)=@_;
@@ -6141,36 +7167,14 @@ sub goodbye {
&logthis(sprintf("%-20s is %s",'hits',$hits));
&flushcourselogs();
&logthis("Shutting down");
- return DONE;
}
BEGIN {
# ----------------------------------- Read loncapa.conf and loncapa_apache.conf
unless ($readit) {
{
- # FIXME: Use LONCAPA::Configuration::read_conf here and omit next block
- open(my $config,") {
- if ($configline=~/\S/ && $configline =~ /^[^\#]*PerlSetVar/) {
- my ($dummy,$varname,$varvalue)=split(/\s+/,$configline);
- chomp($varvalue);
- $perlvar{$varname}=$varvalue;
- }
- }
- close($config);
-}
-{
- open(my $config,") {
- if ($configline =~ /^[^\#]*PerlSetVar/) {
- my ($dummy,$varname,$varvalue)=split(/\s+/,$configline);
- chomp($varvalue);
- $perlvar{$varname}=$varvalue;
- }
- }
- close($config);
+ my $configvars = LONCAPA::Configuration::read_conf('loncapa.conf');
+ %perlvar = (%perlvar,%{$configvars});
}
# ------------------------------------------------------------ Read domain file
@@ -6185,7 +7189,7 @@ BEGIN {
# next if /^\#/;
chomp;
my ($domain, $domain_description, $def_auth, $def_auth_arg,
- $def_lang, $city, $longi, $lati) = split(/:/,$_);
+ $def_lang, $city, $longi, $lati, $primary) = split(/:/,$_);
$domain_auth_def{$domain}=$def_auth;
$domain_auth_arg_def{$domain}=$def_auth_arg;
$domaindescription{$domain}=$domain_description;
@@ -6193,6 +7197,7 @@ BEGIN {
$domain_city{$domain}=$city;
$domain_longi{$domain}=$longi;
$domain_lati{$domain}=$lati;
+ $domain_primary{$domain}=$primary;
# &logthis("Domain.tab: $domain, $domain_auth_def{$domain}, $domain_auth_arg_def{$domain},$domaindescription{$domain}");
# &logthis("Domain.tab: $domain ".$domaindescription{$domain} );
@@ -6251,7 +7256,9 @@ sub get_iphost {
while (my $configline=<$config>) {
chomp($configline);
if ($configline) {
- $spareid{$configline}=1;
+ my ($host,$type) = split(':',$configline,2);
+ if (!defined($type) || $type eq '') { $type = 'default' };
+ push(@{ $spareid{$type} }, $host);
}
}
close($config);
@@ -6277,8 +7284,14 @@ sub get_iphost {
while (my $configline=<$config>) {
chomp($configline);
if ($configline) {
- my ($short,$plain)=split(/:/,$configline);
- if ($plain ne '') { $prp{$short}=$plain; }
+ my ($short,@plain)=split(/:/,$configline);
+ %{$prp{$short}} = ();
+ if (@plain > 0) {
+ $prp{$short}{'std'} = $plain[0];
+ for (my $i=1; $i<@plain; $i++) {
+ $prp{$short}{'alt'.$i} = $plain[$i];
+ }
+ }
}
}
close($config);
@@ -6573,6 +7586,7 @@ actions
'': forbidden
1: user needs to choose course
2: browse allowed
+ A: passphrase authentication needed
=item *
@@ -6893,6 +7907,27 @@ all args are optional
=item *
+dumpstore($namespace,$udom,$uname,$regexp,$range) :
+dumps the complete (or key matching regexp) namespace into a hash
+($udom, $uname, $regexp, $range are optional) for a namespace that is
+normally &store()ed into
+
+$range should be either an integer '100' (give me the first 100
+ matching records)
+ or be two integers sperated by a - with no spaces
+ '30-50' (give me the 30th through the 50th matching
+ records)
+
+
+=item *
+
+putstore($namespace,$symb,$version,$storehash,$udomain,$uname) :
+replaces a &store() version of data with a replacement set of data
+for a particular resource in a namespace passed in the $storehash hash
+reference
+
+=item *
+
tmpstore($storehash,$symb,$namespace,$udom,$uname) : storage that
works very similar to store/cstore, but all data is stored in a
temporary location and can be reset using tmpreset, $storehash should
@@ -6922,10 +7957,15 @@ namesp ($udom and $uname are optional)
=item *
-dump($namespace,$udom,$uname,$regexp) :
+dump($namespace,$udom,$uname,$regexp,$range) :
dumps the complete (or key matching regexp) namespace into a hash
-($udom, $uname and $regexp are optional)
+($udom, $uname, $regexp, $range are optional)
+$range should be either an integer '100' (give me the first 100
+ matching records)
+ or be two integers sperated by a - with no spaces
+ '30-50' (give me the 30th through the 50th matching
+ records)
=item *
inc($namespace,$store,$udom,$uname) : increments $store in $namespace.
@@ -6941,19 +7981,33 @@ put($namespace,$storehash,$udom,$uname)
=item *
-putstore($namespace,$storehash,$udomain,$uname) : stores hash in namesp
-keys used in storehash include version information (e.g., 1:$symb:message etc.) as
-used in records written by &store and retrieved by &restore. This function
-was created for use in editing discussion posts, without incrementing the
-version number included in the key for a particular post. The colon
-separated list of attribute names (e.g., the value associated with the key
-1:keys:$symb) is also generated and passed in the ampersand separated
-items sent to lonnet::reply().
+cput($namespace,$storehash,$udom,$uname) : critical put
+($udom and $uname are optional)
=item *
-cput($namespace,$storehash,$udom,$uname) : critical put
-($udom and $uname are optional)
+newput($namespace,$storehash,$udom,$uname) :
+
+Attempts to store the items in the $storehash, but only if they don't
+currently exist, if this succeeds you can be certain that you have
+successfully created a new key value pair in the $namespace db.
+
+
+Args:
+ $namespace: name of database to store values to
+ $storehash: hashref to store to the db
+ $udom: (optional) domain of user containing the db
+ $uname: (optional) name of user caontaining the db
+
+Returns:
+ 'ok' -> succeeded in storing all keys of $storehash
+ 'key_exists: ' -> failed to anything out of $storehash, as at
+ least already existed in the db (other
+ requested keys may also already exist)
+ 'error: ' -> unable to tie the DB or other erorr occured
+ 'con_lost' -> unable to contact request server
+ 'refused' -> action was not allowed by remote machine
+
=item *
@@ -7081,6 +8135,16 @@ getfile($file,$caller) : two cases - req
- returns the entire contents of a file or -1;
it properly subscribes to and replicates the file if neccessary.
+
+=item *
+
+stat_file($url) : $url is expected to be a /res/ or /uploaded/ style file
+ reference
+
+returns either a stat() list of data about the file or an empty list
+if the file doesn't exist or couldn't find out about it (connection
+problems or user unknown)
+
=item *
filelocation($dir,$file) : returns file system location of a file
@@ -7181,6 +8245,94 @@ removeuploadedurl(): convience function
Args:
url: a full /uploaded/... url to delete
+=item *
+
+get_portfile_permissions():
+ Args:
+ domain: domain of user or course contain the portfolio files
+ user: name of user or num of course contain the portfolio files
+ Returns:
+ hashref of a dump of the proper file_permissions.db
+
+
+=item *
+
+get_access_controls():
+
+Args:
+ current_permissions: the hash ref returned from get_portfile_permissions()
+ group: (optional) the group you want the files associated with
+ file: (optional) the file you want access info on
+
+Returns:
+ a hash (keys are file names) of hashes containing
+ keys are: path to file/file_name\0uniqueID:scope_end_start (see below)
+ values are XML containing access control settings (see below)
+
+Internal notes:
+
+ access controls are stored in file_permissions.db as key=value pairs.
+ key -> path to file/file_name\0uniqueID:scope_end_start
+ where scope -> public,guest,course,group,domains or users.
+ end -> UNIX time for end of access (0 -> no end date)
+ start -> UNIX time for start of access
+
+ value -> XML description of access control
+ (type =1 of: public,guest,course,group,domains,users">
+
+
+
+ for scope type = guest
+
+ for scope type = course or group
+
+
+
+
+
+
+
+
+ for scope type = domains
+
+ for scope type = users
+
+
+
+
+
+
+
+ Access data is also aggregated for each file in an additional key=value pair:
+ key -> path to file/file_name\0accesscontrol
+ value -> reference to hash
+ hash contains key = value pairs
+ where key = uniqueID:scope_end_start
+ value = UNIX time record was last updated
+
+ Used to improve speed of look-ups of access controls for each file.
+
+ Locks on files (resulting from submission of portfolio file to a homework problem stored in array of arrays.
+
+modify_access_controls():
+
+Modifies access controls for a portfolio file
+Args
+1. file name
+2. reference to hash of required changes,
+3. domain
+4. username
+ where domain,username are the domain of the portfolio owner
+ (either a user or a course)
+
+Returns:
+1. result of additions or updates ('ok' or 'error', with error message).
+2. result of deletions ('ok' or 'error', with error message).
+3. reference to hash of any new or updated access controls.
+4. reference to hash used to map incoming IDs to uniqueIDs assigned to control.
+ key = integer (inbound ID)
+ value = uniqueID
+
=back
=head2 HTTP Helper Routines