--- loncom/interface/loncoursequeueadmin.pm	2014/04/16 13:32:43	1.45
+++ loncom/interface/loncoursequeueadmin.pm	2017/08/03 16:11:46	1.57
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Utilities to administer domain course requests and course self-enroll requests
 #
-# $Id: loncoursequeueadmin.pm,v 1.45 2014/04/16 13:32:43 raeburn Exp $
+# $Id: loncoursequeueadmin.pm,v 1.57 2017/08/03 16:11:46 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -105,13 +105,13 @@ sub send_selfserve_notification {
         $rawsubj = 'Self-enrollment requests processed';
         push(@rawmsg,{
                       mt => 'Enrollment requests in the following course: [_1] have been processed.',
-                      args => ["\n$contextdesc"],
+                      args => ["\n$contextdesc\n"],
                      });
     } elsif ($context eq 'domainmanagers') {
         $rawsubj = 'Course/Community requests reviewed';
         push(@rawmsg,{
-                      mt  => 'Course/Community creation requests in the following domain: "[_1]" have been reviewed.',
-                      args => ["\n$contextdesc"],
+                      mt  => 'Course/Community creation requests in the following domain: [_1] have been reviewed.',
+                      args => ["\n$contextdesc\n"],
                      });
         if (ref($textstr) eq 'ARRAY') {
             push(@rawmsg,@{$textstr});
@@ -119,8 +119,8 @@ sub send_selfserve_notification {
     } elsif ($context eq 'authormanagers') {
         $rawsubj = 'Authoring Space requests reviewed';
         push(@rawmsg,{
-                      mt  => 'Authoring requests in the following domain: "[_1]" have been reviewed.',
-                      args => ["\n$contextdesc"],
+                      mt  => 'Authoring requests in the following domain: [_1] have been reviewed.',
+                      args => ["\n$contextdesc\n"],
                      });
         if (ref($textstr) eq 'ARRAY') {
             push(@rawmsg,@{$textstr});
@@ -128,8 +128,8 @@ sub send_selfserve_notification {
     } elsif ($context eq 'usernamemanagers') {
         $rawsubj = 'LON-CAPA account requests reviewed';
         push(@rawmsg,{
-                      mt  => 'Account requests in the following domain: "[_1]" have been reviewed.',
-                      args => ["\n$contextdesc"],
+                      mt  => 'Account requests in the following domain: [_1] have been reviewed.',
+                      args => ["\n$contextdesc\n"],
                      });
         if (ref($textstr) eq 'ARRAY') {
             push(@rawmsg,@{$textstr});
@@ -396,6 +396,8 @@ sub display_queued_requests {
         if ($context eq 'pending') {
             $disposition = 'pending';
             $nextphase = 'requestvalidation';
+        } elsif ($context eq 'displaypending') {
+            $disposition = 'pending';
         }
         %requesthash = &Apache::lonnet::dump_dom($namespace,$dom,'_'.$disposition);
         $nextelement = '<input type="hidden" name="phase" value="'.$nextphase.'" />';
@@ -422,7 +424,7 @@ sub display_queued_requests {
                     my ($cnum,$disposition) = split('_',$item);
                     $entry = $cnum.':'.$requesthash{$item}{'ownername'}.':'.
                              $requesthash{$item}{'ownerdom'}.':';
-                    if ($context eq 'pending') {
+                    if (($context eq 'pending') || ($context eq 'displaypending')) {
                         $entry .= $requesthash{$item}{'instcode'};
                     } else {
                         $entry .= $requesthash{$item}{'crstype'};
@@ -441,10 +443,12 @@ sub display_queued_requests {
         if (keys(%queue_by_date) > 0) {
             if ($context eq 'course') {
                 $output .=  '<h3>'.&mt('Self-enrollment requests queued pending approval by a Coordinator').'</h3>';
-            } elsif ($context eq 'pending') {
+            } elsif (($context eq 'pending') || ($context eq 'displaypending')) {
                 $output .= '<h3>'.&mt('Requests for official courses queued pending validation').'</h3>'.
                            '<p>'.&mt('Requests are validated against institutional data to confirm that the requestor is an instructor of record.').'<br />'.
-                           &mt('Validation is attempted when the request is submitted.').' '.&mt('If unvalidated, the request will be held in a queue.').' '.&mt('Validation of pending requests is automatically repeated daily.').'</p>';
+                           &mt('Validation is attempted when the request is submitted.').' '.
+                           &mt('If unvalidated, the request will be held in a queue.').' '.
+                           &mt('Validation of pending requests is automatically repeated daily.').'</p>';
             } elsif ($context eq 'requestauthor') {
                 $output .= '<h3>'.&mt('Requests for Authoring Space queued pending approval by a Domain Coordinator').'</h3>';
             } elsif ($context eq 'requestusername') {
@@ -472,8 +476,9 @@ sub display_queued_requests {
         if ($context eq 'pending') {
             $output .= '<br /><input type="submit" name="validationcheck" value="'.
                        &mt('Validate').'" /><br />'."\n".
-                       '<p>'.&mt('Any course/community requests which are successfully validated will be created immediately.').' '.&mt('Unvalidated requests will be listed for manual approval/rejection.').'</p>';
-        } else {
+                       '<p>'.&mt('Any course/community requests which are successfully validated will be created immediately.').' '.
+                             &mt('Unvalidated requests will be listed for manual approval/rejection.').'</p>';
+        } elsif (($context ne 'helpdesk') && ($context ne 'displaypending')) {
             $output .= '<br /><input type="submit" name="processqueue" value="'.&mt('Save').'" />';
         }
         $output .= '</form>';
@@ -481,7 +486,7 @@ sub display_queued_requests {
         $output .= '<div class="LC_info">';
         if ($context eq 'course') {
             $output .= &mt('There are currently no enrollment requests awaiting approval.');
-        } elsif ($context eq 'pending') {
+        } elsif (($context eq 'pending') || ($context eq 'displaypending')) {
             $output .= &mt('There are currently no requests for official courses awaiting validation.');
         } elsif ($context eq 'requestauthor') {
             $output .= &mt('There are currently no requests for Authoring Space awaiting approval.');
@@ -501,7 +506,7 @@ sub build_queue_display {
     my %crstypes;
     my $output =  &Apache::loncommon::start_data_table().
                   &Apache::loncommon::start_data_table_header_row();
-    unless ($context eq 'pending') { 
+    unless (($context eq 'pending') || ($context eq 'displaypending') || ($context eq 'helpdesk')) { 
         $output .= '<th>'.&mt('Action').'</th>';
     }
     $output .= '<th>'.&mt('Requestor').'</th>';
@@ -513,7 +518,7 @@ sub build_queue_display {
     } elsif ($context eq 'requestusername') {
         $output .= '<th>'.&mt('Date requested').'</th>'.
                    '<th>'.&mt('Details').'</th>';
-    } elsif ($context eq 'pending' || $context eq 'stillpending') {
+    } elsif ($context eq 'pending' || $context eq 'displaypending' || $context eq 'stillpending') {
         $output .= '<th>'.&mt('Institutional code').'</th>'.
                    '<th>'.&mt('Date requested').'</th>'.
                    '<th>'.&mt('Details').'</th>';
@@ -523,6 +528,7 @@ sub build_queue_display {
                         unofficial => 'Unofficial course',
                         community  => 'Community',
                         textbook   => 'Textbook course',
+                        placement  => 'Placement test',
                     );
         $output .= '<th>'.&mt('Type').'</th>'.
                    '<th>'.&mt('Date requested').'</th>'.
@@ -568,7 +574,7 @@ sub build_queue_display {
                 } else {
                     my ($cnum,$ownername,$ownerdom,$type,$cdesc);
                     my $queued = 'approval'; 
-                    if ($context eq 'pending' || $context eq 'stillpending') {
+                    if ($context eq 'pending' || $context eq 'displaypending' || $context eq 'stillpending') {
                         ($cnum,$ownername,$ownerdom,$instcode,$cdesc)=split(/:/,$request,5);
                         $queued = 'pending';                        
                     } else {
@@ -586,7 +592,7 @@ sub build_queue_display {
                                 &Apache::loncommon::plainname($ownername,$ownerdom),
                                 $ownername,$ownerdom);
                 }
-                unless ($context eq 'pending') {
+                unless (($context eq 'pending') || ($context eq 'displaypending') || ($context eq 'helpdesk')) {
                     $row = '<td><span class="LC_nobreak"><label>'.
                            '<input type="radio" value="'.$approve.'" name="'.$count.'radioreq" />'.&mt('Approve').'</label>'.
                            '<label>'.('&nbsp;'x2).
@@ -605,7 +611,7 @@ sub build_queue_display {
                     $row .= '<td>'.$showtime.'</td>'."\n".
                             '<td>'.$detailslink.'</td>'."\n";
                 } else { 
-                    if ($context eq 'pending' || $context eq 'stillpending') {
+                    if ($context eq 'pending' || $context eq 'displaypending' || $context eq 'stillpending') {
                         $row .= '<td>'.$instcode.'</td>'."\n";
                     } else {
                         $row .= '<td>'.$crstype.'</td>'."\n";
@@ -651,7 +657,7 @@ sub update_request_queue {
         $beneficiary = 'enroller';
         $cid = $env{'request.course.id'};
         $crstype = lc(&Apache::loncommon::course_type());
-        $firsturl = &course_portal_url($cnum,$cdom);
+        $firsturl = &Apache::lonnet::course_portal_url($cnum,$cdom);
         %requesthash = &Apache::lonnet::dump($namespace,$cdom,$cnum);
         $access_start =  $env{'course.'.$cid.'.internal.selfenroll_start_access'};
         $access_end =  $env{'course.'.$cid.'.internal.selfenroll_end_access'};
@@ -680,7 +686,7 @@ sub update_request_queue {
             }
         }
         my $domconfiguser = &Apache::lonnet::get_domainconfiguser($cdom);
-        $firsturl = &course_portal_url($domconfiguser,$cdom);
+        $firsturl = &Apache::lonnet::course_portal_url($domconfiguser,$cdom);
         $approvedmsg = [{
                             mt => 'Your request for Authoring Space has been approved.',
                         },
@@ -705,7 +711,7 @@ sub update_request_queue {
             }
         }
         my $domconfiguser = &Apache::lonnet::get_domainconfiguser($cdom);
-        $firsturl = &course_portal_url($domconfiguser,$cdom);
+        $firsturl = &Apache::lonnet::course_portal_url($domconfiguser,$cdom);
         $approvedmsg = [{
                             mt => 'Your request for a LON-CAPA account has been approved.',
                         },
@@ -886,7 +892,7 @@ sub update_request_queue {
                 $middlename = $curr{$uname}{'middlename'};
                 $lastname   = $curr{$uname}{'lastname'};
                 $generation = $curr{$uname}{'generation'};
-                $inststatus = $curr{$uname}{'usertype'};
+                $inststatus = $curr{$uname}{'inststatus'};
 
                 my ($key,$caller)=split(/&/,$curr{$uname}{'tmpinfo'});
                 if ($caller eq 'createaccount') {
@@ -968,6 +974,9 @@ sub update_request_queue {
                                     }
                                 }
                             }
+                            if ($history{'details'}{'clonecrs'}) {
+                                $customitems{'_LC_clonefrom'} = $history{'details'}{'clonedom'}.'_'.$history{'details'}{'clonecrs'};
+                            }
                             my ($result,$postprocess) = &course_creation($cdom,$cnum,$context,$history{'details'},\$logmsg,
                                                         \$newusermsg,\$addresult,\$enrollcount,
                                                         \$response,\$keysmsg,\%domdefs,$longroles,\$code,\%customitems);
@@ -977,7 +986,7 @@ sub update_request_queue {
                                 } else {
                                     $approvedmsg = $approvalmsg{'course'};
                                 }
-                                my $firsturl = &course_portal_url($cnum,$cdom);
+                                my $firsturl = &Apache::lonnet::course_portal_url($cnum,$cdom);
                                 if (ref($approvedmsg) eq 'ARRAY') {
                                     if (ref($approvedmsg->[1]) eq 'HASH') {
                                         $approvedmsg->[1]->{'args'} = [$firsturl];
@@ -1311,7 +1320,7 @@ sub update_request_queue {
                         my $syllabuslink =
                             &Apache::loncommon::syllabuswrapper($showcourse,$cnum,$cdom);
                         if ($codes{$cnum}) {
-                            $syllabuslink .= &mt('Unique code: [_1]',$codes{$cnum});
+                            $syllabuslink .= ' '.&mt('Unique code: [_1]',$codes{$cnum});
                         }
                         $output .= '<li>'.$syllabuslink.'</li>';
                     }
@@ -1561,22 +1570,6 @@ sub update_request_queue {
     return $output;
 }
 
-sub course_portal_url {
-    my ($cnum,$cdom) = @_;
-    my $chome = &Apache::lonnet::homeserver($cnum,$cdom);
-    my $hostname = &Apache::lonnet::hostname($chome);
-    my $protocol = $Apache::lonnet::protocol{$chome};
-    $protocol = 'http' if ($protocol ne 'https');
-    my %domdefaults = &Apache::lonnet::get_domain_defaults($cdom);
-    my $firsturl;
-    if ($domdefaults{'portal_def'}) {
-        $firsturl = $domdefaults{'portal_def'};
-    } else {
-        $firsturl = $protocol.'://'.$hostname;
-    }
-    return $firsturl;
-}
-
 sub get_student_counts {
     my ($cdom,$cnum) = @_;
     my (%idx,%stucounts);
@@ -1600,12 +1593,14 @@ sub course_creation {
         $keysmsg,$domdefs,$longroles,$coderef,$customhash) =  @_;
     unless ((ref($details) eq 'HASH') && (ref($domdefs) eq 'HASH') && 
             (ref($longroles) eq 'HASH')) {
-        return 'error: Invalid request';
+        return ('error: Invalid request');
     }
     my ($result,$ownername,$ownerdom);
     my $crstype = $details->{'crstype'};
     my $coursedesc = $details->{'cdescr'};
-    my %domconfig = &Apache::lonnet::get_dom('configuration',['requestauthor'],$dom);
+    my $accessstart = $details->{'accessstart'};
+    my $accessend = $details->{'accessend'};
+    my %domconfig = &Apache::lonnet::get_dom('configuration',['requestcourses'],$dom);
     if (ref($domconfig{'requestcourses'}) eq 'HASH') {
         if (ref($domconfig{'requestcourses'}{'uniquecode'}) eq 'HASH') {
             if ($domconfig{'requestcourses'}{'uniquecode'}{$crstype}) {
@@ -1639,7 +1634,8 @@ sub course_creation {
             $code = $$coderef;
         }
         $postprocess = &Apache::lonnet::auto_crsreq_update($dom,$cnum,$crstype,$result,$ownername,
-                                                           $ownerdom,$fullname,$coursedesc,$code,$customhash);
+                                                           $ownerdom,$fullname,$coursedesc,$code,
+                                                           $accessstart,$accessend,$customhash);
     } else {
         $result = 'error: '.$cid;
     }
@@ -1668,6 +1664,8 @@ sub build_batchcreatehash {
         $batchhash{'authparam'} = $domdefs->{'auth_arg_def'};
         if ($details->{'crstype'} eq 'community') {
             $batchhash{'crstype'} = 'Community';
+        } elsif ($details->{'crstype'} eq 'placement') {
+            $batchhash{'crstype'} = 'Placement';
         } else {
             if ($details->{'crstype'} eq 'textbook') {
                 if ($details->{'clonecrs'} && $details->{'clonedom'}) {
@@ -1713,12 +1711,13 @@ sub build_batchcreatehash {
         $batchhash{'users'}{$owner}{lastname} = $owner_lastname;
         $batchhash{'users'}{$owner}{emailenc} = $emailenc;
         $batchhash{'users'}{$owner}{owneremail} = $owneremail;
+        $batchhash{'setcomment'} = 1;
     }
     return %batchhash;
 }
 
 sub can_clone_course {
-    my ($uname,$udom,$clonecrs,$clonedom,$crstype) = @_;
+    my ($uname,$udom,$clonecrs,$clonedom,$crstype,$dom,$instcode) = @_;
     my $canclone;
     my $ccrole = 'cc';
     if ($crstype eq 'community') {
@@ -1729,19 +1728,70 @@ sub can_clone_course {
     if (exists($roleshash{$clonecrs.':'.$clonedom.':'.$ccrole})) {
         $canclone = 1;
     } else {
-        my %courseenv = &Apache::lonnet::userenvironment($clonedom,$clonecrs,('cloners'));
+        my %courseenv = &Apache::lonnet::userenvironment($clonedom,$clonecrs,
+                                                         ('cloners','internal.coursecode'));
         my $cloners = $courseenv{'cloners'};
+        my $clonefromcode = $courseenv{'internal.coursecode'};
         if ($cloners ne '') {
             my @cloneable = split(',',$cloners);
             if (grep(/^\*$/,@cloneable)) {
                 $canclone = 1;
-            }
-            if (grep(/^\*:\Q$udom\E$/,@cloneable)) {
+            } elsif (grep(/^\*:\Q$udom\E$/,@cloneable)) {
                 $canclone = 1;
-            }
-            if (grep(/^\Q$uname\E:\Q$udom\E$/,@cloneable)) {
+            } elsif (grep(/^\Q$uname\E:\Q$udom\E$/,@cloneable)) {
                 $canclone = 1;
             }
+            unless ($canclone) {
+                if (($clonefromcode) && ($instcode) && ($clonedom eq $dom)) {
+                    my (%gotdomdefaults,%gotcodedefaults);
+                    foreach my $cloner (@cloneable) {
+                        if (($cloner ne '*') && ($cloner !~ /^\*\:$match_domain$/) &&
+                            ($cloner !~ /^$match_username\:$match_domain$/) && ($cloner ne '')) {
+                            if ($cloner =~ /\=/) {
+                                my (%codedefaults,@code_order);
+                                if (ref($gotcodedefaults{$clonedom}) eq 'HASH') {
+                                    if (ref($gotcodedefaults{$clonedom}{'defaults'}) eq 'HASH') {
+                                        %codedefaults = %{$gotcodedefaults{$clonedom}{'defaults'}};
+                                    }
+                                    if (ref($gotcodedefaults{$clonedom}{'order'}) eq 'ARRAY') {
+                                        @code_order = @{$gotcodedefaults{$dom}{'order'}};
+                                    }
+                                } else {
+                                    &Apache::lonnet::auto_instcode_defaults($clonedom,
+                                                                            \%codedefaults,
+                                                                            \@code_order);
+                                    $gotcodedefaults{$clonedom}{'defaults'} = \%codedefaults;
+                                    $gotcodedefaults{$clonedom}{'order'} = \@code_order;
+                                }
+                                if (@code_order > 0) {
+                                    if (&Apache::lonnet::check_instcode_cloning(\%codedefaults,\@code_order,
+                                                                                $cloner,$clonefromcode,$instcode)) {
+                                        $canclone = 1;
+                                        last; 
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } else {
+            my %domdefs = &Apache::lonnet::get_domain_defaults($clonedom);
+            if ($domdefs{'canclone'}) {
+                unless ($domdefs{'canclone'} eq 'none') {
+                    if ($domdefs{'canclone'} eq 'domain') {
+                        if ($udom eq $clonedom) {
+                            $canclone = 1;
+                        }
+                    } elsif (($clonefromcode) && ($instcode) &&
+                             ($clonedom eq $dom)) {
+                        if (&Apache::lonnet::default_instcode_cloning($clonedom,$domdefs{'canclone'},
+                                                                      $clonefromcode,$instcode)) {
+                            $canclone = 1;
+                        }
+                    }
+                }
+            }
         }
         unless ($canclone) {
             if (&Apache::lonnet::is_course_owner($clonedom,$clonecrs,$uname,$udom)) {
@@ -2023,6 +2073,9 @@ sub process_official_reqs {
                             }
                         }
                     }
+                    if ($history{'details'}{'clonecrs'}) {
+                        $customitems{'_LC_clonefrom'} = $history{'details'}{'clonedom'}.'_'.$history{'details'}{'clonecrs'};
+                    }
                     my ($result,$postprocess) = 
                         &course_creation($dom,$cnum,'domain',$history{'details'},\$logmsg,\$newusermsg,\$addresult,
                                          \$enrollcount,\$response,\$keysmsg,\%domdefs,\%longroles,\$code,\%customitems);
@@ -2032,7 +2085,7 @@ sub process_official_reqs {
                         my $cid = $dom.'_'.$cnum;
                         push(@{$newcids{$instcode}},$cid);
                         if ($dcname && $dcdom) {
-                            my $firsturl = &course_portal_url($cnum,$dom);
+                            my $firsturl = &Apache::lonnet::course_portal_url($cnum,$dom);
                             my $beneficiary = 'pendingrequestor';
                             my $now = time;
                             my $owner = $ownername.':'.$ownerdom;
@@ -2059,6 +2112,11 @@ sub process_official_reqs {
                                         }
                                     }
                                 }
+                                if (ref($postprocess->{'createdactions'}) eq 'HASH') {
+                                    if (ref($postprocess->{'createdactions'}{'environment'}) eq 'HASH') {
+                                        &postprocess_crsenv($dom,$cnum,$postprocess->{'createdactions'}{'environment'});
+                                    }
+                                }
                             }
                             &send_selfserve_notification($owner,$approvedmsg,
                                                          $cid,$cdescr,$now,
@@ -2130,6 +2188,56 @@ sub process_official_reqs {
     return $output;
 }
 
+sub postprocess_crsenv {
+    my ($dom,$cnum,$postprocessenv) = @_;
+    if (ref($postprocessenv) eq 'HASH') {    
+        my $cid = $dom.'_'.$cnum;
+        my %settablecrsenv = (
+                              'internal.selfenroll_types'        => 1,
+                              'internal.selfenroll_registered'   => 1,
+                              'internal.selfenroll_section'      => 1,
+                              'internal.selfenroll_start_access' => 1,
+                              'internal.selfenroll_end_access'   => 1,
+                              'internal.selfenroll_limit'        => 1,
+                              'internal.selfenroll_cap'          => 1,
+                              'internal.selfenroll_approval'     => 1,
+                              'internal.selfenroll_notifylist'   => 1,
+                             );
+        my %needcrsidput = (
+                              'internal.selfenroll_types'      => 1,
+                              'internal.selfenroll_start_date' => 1,
+                              'internal. selfenroll_end_date'  => 1,
+                           );
+        my (@needupdate,%newcrsenv);
+        foreach my $key (keys(%{$postprocessenv})) { 
+            if ($settablecrsenv{$key}) {
+                $newcrsenv{$key} = $postprocessenv->{$key};
+                if ($needcrsidput{$key}) {
+                    push(@needupdate,$key); 
+                }
+            }
+            if (keys(%newcrsenv)) {
+                my $putresult = &Apache::lonnet::put('environment',\%newcrsenv,$dom,$cnum);
+                if ($putresult eq 'ok') {
+                    if (@needupdate) {
+                        my %crsinfo =
+                            &Apache::lonnet::courseiddump($dom,'.',1,'.','.',$cnum,undef,undef,'.');
+                        if (ref($crsinfo{$cid}) eq 'HASH') {
+                            foreach my $key (@needupdate) {
+                                $crsinfo{$cid}{$key} = $newcrsenv{$key};
+                            }
+                            my $chome = &Apache::lonnet::homeserver($cnum,$dom);
+                            &Apache::lonnet::courseidput($dom,\%crsinfo,$chome,'notime');
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return;
+}
+
+
 sub requestcourses_validation_types {
     my @items = ('url','fields','button','markup');
     my %names =  &Apache::lonlocal::texthash (