--- loncom/lonnet/perl/lonnet.pm	2010/09/27 01:41:33	1.1056.4.10
+++ loncom/lonnet/perl/lonnet.pm	2010/09/24 13:15:47	1.1085
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.1056.4.10 2010/09/27 01:41:33 raeburn Exp $
+# $Id: lonnet.pm,v 1.1085 2010/09/24 13:15:47 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -263,7 +263,7 @@ sub get_server_homeID {
     }
     my $cachetime = 12*3600;
     my $serverhomeID;
-    if ($caller eq 'loncron') {
+    if ($caller eq 'loncron') { 
         my @machine_ids = &machine_ids($hostname);
         foreach my $id (@machine_ids) {
             my $response = &reply('serverhomeID',$id);
@@ -724,49 +724,25 @@ sub userload {
     return $userloadpercent;
 }
 
-# ------------------------------------------ Fight off request when overloaded
-
-sub overloaderror {
-    my ($r,$checkserver)=@_;
-    unless ($checkserver) { $checkserver=$perlvar{'lonHostID'}; }
-    my $loadavg;
-    if ($checkserver eq $perlvar{'lonHostID'}) {
-       open(my $loadfile,'/proc/loadavg');
-       $loadavg=<$loadfile>;
-       $loadavg =~ s/\s.*//g;
-       $loadavg = 100*$loadavg/$perlvar{'lonLoadLim'};
-       close($loadfile);
-    } else {
-       $loadavg=&reply('load',$checkserver);
-    }
-    my $overload=$loadavg-100;
-    if ($overload>0) {
-	$r->err_headers_out->{'Retry-After'}=$overload;
-        $r->log_error('Overload of '.$overload.' on '.$checkserver);
-        return 413;
-    }    
-    return '';
-}
-
 # ------------------------------ Find server with least workload from spare.tab
 
 sub spareserver {
-    my ($loadpercent,$userloadpercent,$want_server_name) = @_;
+    my ($loadpercent,$userloadpercent,$want_server_name,$udom) = @_;
     my $spare_server;
     if ($userloadpercent !~ /\d/) { $userloadpercent=0; }
     my $lowest_load=($loadpercent > $userloadpercent) ? $loadpercent 
                                                      :  $userloadpercent;
     my ($uint_dom,$remotesessions);
-    if ($env{'user.domain'}) {
-        my $uprimary_id = &Apache::lonnet::domain($env{'user.domain'},'primary');
+    if (($udom ne '') && (&domain($udom) ne '')) {
+        my $uprimary_id = &Apache::lonnet::domain($udom,'primary');
         $uint_dom = &Apache::lonnet::internet_dom($uprimary_id);
-        my %udomdefaults = &Apache::lonnet::get_domain_defaults($env{'user.domain'});
+        my %udomdefaults = &Apache::lonnet::get_domain_defaults($udom);
         $remotesessions = $udomdefaults{'remotesessions'};
     }
     foreach my $try_server (@{ $spareid{'primary'} }) {
         if ($uint_dom) {
-            next unless (&spare_can_host($env{'user.domain'},$uint_dom,
-                                         $remotesessions,$try_server));
+             next unless (&spare_can_host($udom,$uint_dom,$remotesessions,
+                                          $try_server));
         }
 	($spare_server, $lowest_load) =
 	    &compare_server_load($try_server, $spare_server, $lowest_load);
@@ -777,8 +753,8 @@ sub spareserver {
     if (!$found_server) {
 	foreach my $try_server (@{ $spareid{'default'} }) {
             if ($uint_dom) {
-                next unless (&spare_can_host($env{'user.domain'},$uint_dom,
-                                             $remotesessions,$try_server));
+                next unless (&spare_can_host($udom,$uint_dom,$remotesessions,
+                                             $try_server));
             }
 	    ($spare_server, $lowest_load) =
 		&compare_server_load($try_server, $spare_server, $lowest_load);
@@ -807,7 +783,7 @@ sub compare_server_load {
     my $userloadans = &reply('userload',$try_server);
 
     if ($loadans !~ /\d/ && $userloadans !~ /\d/) {
-        return; #didn't get a number from the server
+	return; #didn't get a number from the server
     }
 
     my $load;
@@ -1757,8 +1733,7 @@ sub getsection {
     # If there is a role which has expired, return it.
     #
     $courseid = &courseid_to_courseurl($courseid);
-    my $extra = &freeze_escape({'skipcheck' => 1});
-    my %roleshash = &dump('roles',$udom,$unam,$courseid,undef,$extra);
+    my %roleshash = &dump('roles',$udom,$unam,$courseid);
     foreach my $key (keys(%roleshash)) {
         next if ($key !~/^\Q$courseid\E(?:\/)*(\w+)*\_st$/);
         my $section=$1;
@@ -3061,8 +3036,7 @@ sub get_my_roles {
     unless (defined($udom)) { $udom=$env{'user.domain'}; }
     my (%dumphash,%nothide);
     if ($context eq 'userroles') { 
-        my $extra = &freeze_escape({'skipcheck' => 1});
-        %dumphash = &dump('roles',$udom,$uname,'.',undef,$extra);
+        %dumphash = &dump('roles',$udom,$uname);
     } else {
         %dumphash=
             &dump('nohist_userroles',$udom,$uname);
@@ -3390,7 +3364,7 @@ sub get_domain_roles {
     return %personnel;
 }
 
-# ----------------------------------------------------------- Check out an item
+# ----------------------------------------------------------- Interval timing 
 
 sub get_first_access {
     my ($type,$argsymb)=@_;
@@ -3426,91 +3400,6 @@ sub set_first_access {
     return 'already_set';
 }
 
-sub checkout {
-    my ($symb,$tuname,$tudom,$tcrsid)=@_;
-    my $now=time;
-    my $lonhost=$perlvar{'lonHostID'};
-    my $infostr=&escape(
-                 'CHECKOUTTOKEN&'.
-                 $tuname.'&'.
-                 $tudom.'&'.
-                 $tcrsid.'&'.
-                 $symb.'&'.
-		 $now.'&'.$ENV{'REMOTE_ADDR'});
-    my $token=&reply('tmpput:'.$infostr,$lonhost);
-    if ($token=~/^error\:/) { 
-        &logthis("<font color=\"blue\">WARNING: ".
-                "Checkout tmpput failed ".$tudom.' - '.$tuname.' - '.$symb.
-                 "</font>");
-        return ''; 
-    }
-
-    $token=~s/^(\d+)\_.*\_(\d+)$/$1\*$2\*$lonhost/;
-    $token=~tr/a-z/A-Z/;
-
-    my %infohash=('resource.0.outtoken' => $token,
-                  'resource.0.checkouttime' => $now,
-                  'resource.0.outremote' => $ENV{'REMOTE_ADDR'});
-
-    unless (&cstore(\%infohash,$symb,$tcrsid,$tudom,$tuname) eq 'ok') {
-       return '';
-    } else {
-        &logthis("<font color=\"blue\">WARNING: ".
-                "Checkout cstore failed ".$tudom.' - '.$tuname.' - '.$symb.
-                 "</font>");
-    }    
-
-    if (&log($tudom,$tuname,&homeserver($tuname,$tudom),
-                         &escape('Checkout '.$infostr.' - '.
-                                                 $token)) ne 'ok') {
-	return '';
-    } else {
-        &logthis("<font color=\"blue\">WARNING: ".
-                "Checkout log failed ".$tudom.' - '.$tuname.' - '.$symb.
-                 "</font>");
-    }
-    return $token;
-}
-
-# ------------------------------------------------------------ Check in an item
-
-sub checkin {
-    my $token=shift;
-    my $now=time;
-    my ($ta,$tb,$lonhost)=split(/\*/,$token);
-    $lonhost=~tr/A-Z/a-z/;
-    my $dtoken=$ta.'_'.&hostname($lonhost).'_'.$tb;
-    $dtoken=~s/\W/\_/g;
-    my ($dummy,$tuname,$tudom,$tcrsid,$symb,$chtim,$rmaddr)=
-                 split(/\&/,&unescape(&reply('tmpget:'.$dtoken,$lonhost)));
-
-    unless (($tuname) && ($tudom)) {
-        &logthis('Check in '.$token.' ('.$dtoken.') failed');
-        return '';
-    }
-    
-    unless (&allowed('mgr',$tcrsid)) {
-        &logthis('Check in '.$token.' ('.$dtoken.') unauthorized: '.
-                 $env{'user.name'}.' - '.$env{'user.domain'});
-        return '';
-    }
-
-    my %infohash=('resource.0.intoken' => $token,
-                  'resource.0.checkintime' => $now,
-                  'resource.0.inremote' => $ENV{'REMOTE_ADDR'});
-
-    unless (&cstore(\%infohash,$symb,$tcrsid,$tudom,$tuname) eq 'ok') {
-       return '';
-    }    
-
-    if (&log($tudom,$tuname,&homeserver($tuname,$tudom),
-                         &escape('Checkin - '.$token)) ne 'ok') {
-	return '';
-    }
-
-    return ($symb,$tuname,$tudom,$tcrsid);    
-}
-
 # --------------------------------------------- Set Expire Date for Spreadsheet
 
 sub expirespread {
@@ -4126,7 +4015,7 @@ sub rolesinit {
     my ($domain,$username,$authhost)=@_;
     my $now=time;
     my %userroles = ('user.login.time' => $now);
-    my $extra = &freeze_escape({'skipcheck' => 1});
+    my $extra = &freeze_escape({'clientcheckrole' => 1});
     my $rolesdump=reply("dump:$domain:$username:roles:.::$extra",$authhost);
     if (($rolesdump eq 'con_lost') || ($rolesdump eq '') || 
         ($rolesdump =~ /^error:/)) {
@@ -4255,7 +4144,7 @@ sub set_userprivs {
     my $adv=0;
     my %grouproles = ();
     if (keys(%{$allgroups}) > 0) {
-        my @groupkeys;
+        my @groupkeys; 
         foreach my $role (keys(%{$allroles})) {
             push(@groupkeys,$role);
         }
@@ -4331,7 +4220,7 @@ sub role_status {
                             my %userroles = (
                                 'user.role.'.$$role.'.'.$$where => $$tstart.'.'.$$tend
                             );
-                            @rolecodes = ('cm');
+                            @rolecodes = ('cm'); 
                             my $spec=$$role.'.'.$$where;
                             my ($tdummy,$tdomain,$trest)=split(/\//,$$where);
                             if ($$role =~ /^cr\//) {
@@ -4348,7 +4237,7 @@ sub role_status {
                                 my %course_roles = &get_my_roles($env{'user.name'},$env{'user.domain'},'userroles',['active'],['cc','co','in','ta','ep','ad','st','cr'],[$tdomain],1);
                                 if (keys(%course_roles) > 0) {
                                     my ($tnum) = ($trest =~ /^($match_courseid)/);
-                                    if ($tdomain ne '' && $tnum ne '') {
+                                    if ($tdomain ne '' && $tnum ne '') { 
                                         foreach my $key (keys(%course_roles)) {
                                             if ($key =~ /^\Q$tnum\E:\Q$tdomain\E:([^:]+):?([^:]*)/) {
                                                 my $crsrole = $1;
@@ -4477,7 +4366,7 @@ sub del {
 # -------------------------------------------------------------- dump interface
 
 sub dump {
-    my ($namespace,$udomain,$uname,$regexp,$range,$extra)=@_;
+    my ($namespace,$udomain,$uname,$regexp,$range)=@_;
     if (!$udomain) { $udomain=$env{'user.domain'}; }
     if (!$uname) { $uname=$env{'user.name'}; }
     my $uhome=&homeserver($uname,$udomain);
@@ -4486,7 +4375,7 @@ sub dump {
     } else {
 	$regexp='.';
     }
-    my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range:$extra",$uhome);
+    my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range",$uhome);
     my @pairs=split(/\&/,$rep);
     my %returnhash=();
     foreach my $item (@pairs) {
@@ -5031,7 +4920,7 @@ sub is_portfolio_file {
 }
 
 sub usertools_access {
-    my ($uname,$udom,$tool,$action,$context,$userenvref,$domdefref,$is_advref) = @_;
+    my ($uname,$udom,$tool,$action,$context,$userenvref,$domdefref,$is_advref)=@_;
     my ($access,%tools);
     if ($context eq '') {
         $context = 'tools';
@@ -5178,7 +5067,7 @@ sub is_advanced_user {
     my ($udom,$uname) = @_;
     if ($udom ne '' && $uname ne '') {
         if (($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) {
-            return $env{'user.adv'};
+            return $env{'user.adv'};  
         }
     }
     my %roleshash = &get_my_roles($uname,$udom,'userroles',undef,undef,undef,1);
@@ -6466,8 +6355,7 @@ sub get_users_groups {
     } else {  
         $grouplist = '';
         my $courseurl = &courseid_to_courseurl($courseid);
-        my $extra = &freeze_escape({'skipcheck' => 1});
-        my %roleshash = &dump('roles',$udom,$uname,$courseurl,undef,$extra);
+        my %roleshash = &dump('roles',$udom,$uname,$courseurl);
         my $access_end = $env{'course.'.$courseid.
                               '.default_enrollment_end_date'};
         my $now = time;
@@ -6821,7 +6709,7 @@ sub modifyuser {
     }
     &logthis('Call to modify user '.$udom.', '.$uname.', '.$uid.', '.
              $umode.', '.$first.', '.$middle.', '.
-             $last.', '.$gene.'(forceid: '.$forceid.'; candelete: '.$showcandelete.')'.
+	     $last.', '.$gene.'(forceid: '.$forceid.'; candelete: '.$showcandelete.')'.
              (defined($desiredhome) ? ' desiredhome = '.$desiredhome :
                                      ' desiredhome not specified'). 
              ' by '.$env{'user.name'}.' at '.$env{'user.domain'}.
@@ -6967,7 +6855,7 @@ sub modifyuser {
         return 'ok';
     }
     my $reply = &put('environment', \%names, $udom,$uname);
-    if ($reply ne 'ok') {
+    if ($reply ne 'ok') { 
         return 'error: '.$reply;
     }
     my $sqlresult = &update_allusers_table($uname,$udom,\%names);
@@ -8505,6 +8393,7 @@ sub add_prefix_and_part {
 # ---------------------------------------------------------------- Get metadata
 
 my %metaentry;
+my %importedpartids;
 sub metadata {
     my ($uri,$what,$liburi,$prefix,$depthcount)=@_;
     $uri=&declutter($uri);
@@ -8531,6 +8420,10 @@ sub metadata {
 	if (defined($cached)) { return $result->{':'.$what}; }
     }
     {
+# Imported parts would go here
+        my %importedids=();
+        my @origfileimportpartids=();
+        my $importedparts=0;
 #
 # Is this a recursive call for a library?
 #
@@ -8614,27 +8507,55 @@ sub metadata {
 # This is not a package - some other kind of start tag
 #
 		    my $entry=$token->[1];
-		    my $unikey;
-		    if ($entry eq 'import') {
-			$unikey='';
-		    } else {
-			$unikey=$entry;
-		    }
-		    $unikey.=&add_prefix_and_part($prefix,$token->[2]->{'part'});
-
-		    if (defined($token->[2]->{'id'})) { 
-			$unikey.='_'.$token->[2]->{'id'}; 
-		    }
+		    my $unikey='';
 
 		    if ($entry eq 'import') {
 #
 # Importing a library here
 #
+                        my $location=$parser->get_text('/import');
+                        my $dir=$filename;
+                        $dir=~s|[^/]*$||;
+                        $location=&filelocation($dir,$location);
+                       
+                        my $importmode=$token->[2]->{'importmode'};
+                        if ($importmode eq 'problem') {
+# Import as problem/response
+                           $unikey=&add_prefix_and_part($prefix,$token->[2]->{'part'});
+                        } elsif ($importmode eq 'part') {
+# Import as part(s)
+                           $importedparts=1;
+# We need to get the original file and the imported file to get the part order correct
+# Good news: we do not need to worry about nested libraries, since parts cannot be nested
+# Load and inspect original file
+                           if ($#origfileimportpartids<0) {
+                              undef(%importedpartids);
+                              my $origfilelocation=$perlvar{'lonDocRoot'}.&clutter($uri);
+                              my $origfile=&getfile($origfilelocation);
+                              @origfileimportpartids=($origfile=~/<(part|import)[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs);
+                           }
+
+# Load and inspect imported file
+                           my $impfile=&getfile($location);
+                           my @impfilepartids=($impfile=~/<part[^>]*id\s*=\s*[\"\']([^\"\']+)[\"\'][^>]*>/gs);
+                           if ($#impfilepartids>=0) {
+# This problem had parts
+                               $importedpartids{$token->[2]->{'id'}}=join(',',@impfilepartids);
+                           } else {
+# Importing by turning a single problem into a problem part
+# It gets the import-tags ID as part-ID
+                               $unikey=&add_prefix_and_part($prefix,$token->[2]->{'id'});
+                               $importedpartids{$token->[2]->{'id'}}=$token->[2]->{'id'};
+                           }
+                        } else {
+# Normal import
+                           $unikey=&add_prefix_and_part($prefix,$token->[2]->{'part'});
+                           if (defined($token->[2]->{'id'})) {
+                              $unikey.='_'.$token->[2]->{'id'};
+                           }
+                        }
+
 			if ($depthcount<20) {
-			    my $location=$parser->get_text('/import');
-			    my $dir=$filename;
-			    $dir=~s|[^/]*$||;
-			    $location=&filelocation($dir,$location);
 			    my $metadata = 
 				&metadata($uri,'keys', $location,$unikey,
 					  $depthcount+1);
@@ -8642,8 +8563,16 @@ sub metadata {
 				$metaentry{':'.$meta}=$metaentry{':'.$meta};
 				$metathesekeys{$meta}=1;
 			    }
-			}
-		    } else { 
+			
+                        }
+		    } else {
+#
+# Not importing, some other kind of non-package, non-library start tag
+# 
+                        $unikey=$entry.&add_prefix_and_part($prefix,$token->[2]->{'part'});
+                        if (defined($token->[2]->{'id'})) {
+                            $unikey.='_'.$token->[2]->{'id'};
+                        }
 			if (defined($token->[2]->{'name'})) { 
 			    $unikey.='_'.$token->[2]->{'name'}; 
 			}
@@ -8717,6 +8646,22 @@ sub metadata {
 	    grep { ! $seen{$_} ++ } (split(',',$metaentry{':packages'}));
 	$metaentry{':packages'} = join(',',@uniq_packages);
 
+        if ($importedparts) {
+# We had imported parts and need to rebuild partorder
+           $metaentry{':partorder'}='';
+           $metathesekeys{'partorder'}=1;
+           for (my $index=0;$index<$#origfileimportpartids;$index+=2) {
+               if ($origfileimportpartids[$index] eq 'part') {
+# original part, part of the problem
+                  $metaentry{':partorder'}.=','.$origfileimportpartids[$index+1];
+               } else {
+# we have imported parts at this position
+                  $metaentry{':partorder'}.=','.$importedpartids{$origfileimportpartids[$index+1]};
+               }
+           }
+           $metaentry{':partorder'}=~s/^\,//;
+        }
+
 	$metaentry{':keys'} = join(',',keys(%metathesekeys));
 	&metadata_generate_part0(\%metathesekeys,\%metaentry,$uri);
 	$metaentry{':allpossiblekeys'}=join(',',keys %metathesekeys);
@@ -10082,7 +10027,7 @@ sub get_dns {
     }
 
     sub unique_library {
-        #2x reverse removes all hostnames that appear more than once
+	#2x reverse removes all hostnames that appear more than once
         my %unique = reverse &all_library();
         return reverse %unique;
     }
@@ -10112,7 +10057,7 @@ sub get_dns {
 
     sub get_unique_servers {
         my %unique = reverse &get_servers(@_);
-        return reverse %unique;
+	return reverse %unique;
     }
 
     sub host_domain {
@@ -10648,7 +10593,7 @@ $checkdefauth is optional (value is 1 if
    authenticate user using default authentication method, and allow
    account creation if username does not have account in the domain).
 $clientcancheckhost is optional (value is 1 if checking whether the
-   server can host will occur on the client side in lonauth.pm).
+   server can host will occur on the client side in lonauth.pm).   
 
 =item *
 X<homeserver()>