[LON-CAPA-cvs] cvs: doc /loncapafiles loncapafiles.lpml loncom/automation Autocreate.pl batchcreatecourse.pm loncom/html/adm/help/tex Batch_Creation.tex Clone_Tiny_URLs.tex Course_Request_Clone.tex loncom/interface lonclonecourse.pm loncommon.pm loncoursequeueadmin.pm loncreatecourse.pm londocs.pm lonrequestcourse.pm loncom/lonnet/perl lonnet.pm

raeburn raeburn at source.lon-capa.org
Wed Jul 1 16:09:13 EDT 2020


raeburn		Wed Jul  1 20:09:13 2020 EDT

  Added files:                 
    /loncom/html/adm/help/tex	Clone_Tiny_URLs.tex 

  Modified files:              
    /loncom/interface	londocs.pm lonclonecourse.pm loncommon.pm 
                     	loncoursequeueadmin.pm loncreatecourse.pm 
                     	lonrequestcourse.pm 
    /loncom/lonnet/perl	lonnet.pm 
    /loncom/automation	batchcreatecourse.pm Autocreate.pl 
    /loncom/html/adm/help/tex	Course_Request_Clone.tex 
                             	Batch_Creation.tex 
    /doc/loncapafiles	loncapafiles.lpml 
  Log:
  - Bug 6400 
    Options for transfer or creation of tiny URLs when cloning.
  
  
-------------- next part --------------
Index: loncom/interface/londocs.pm
diff -u loncom/interface/londocs.pm:1.670 loncom/interface/londocs.pm:1.671
--- loncom/interface/londocs.pm:1.670	Fri Mar  6 19:40:57 2020
+++ loncom/interface/londocs.pm	Wed Jul  1 20:08:53 2020
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Documents
 #
-# $Id: londocs.pm,v 1.670 2020/03/06 19:40:57 raeburn Exp $
+# $Id: londocs.pm,v 1.671 2020/07/01 20:08:53 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -4860,7 +4860,7 @@
         $r->rflush();
         my $readonly;
         if ($canedit) {
-            my ($numnew,$errors) = &Apache::loncommon::make_short_symbs($cdom,$cnum,$navmap);
+            my ($numnew,$errors) = &Apache::loncommon::get_requested_shorturls($cdom,$cnum,$navmap);
             if ($numnew) {
                 $r->print('<p class="LC_info">'.&mt('Created [quant,_1,URL]',$numnew).'</p>');
             }
Index: loncom/interface/lonclonecourse.pm
diff -u loncom/interface/lonclonecourse.pm:1.14 loncom/interface/lonclonecourse.pm:1.15
--- loncom/interface/lonclonecourse.pm:1.14	Mon Jun  1 20:35:02 2020
+++ loncom/interface/lonclonecourse.pm	Wed Jul  1 20:08:54 2020
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # routines for clone a course
 #
-# $Id: lonclonecourse.pm,v 1.14 2020/06/01 20:35:02 raeburn Exp $
+# $Id: lonclonecourse.pm,v 1.15 2020/07/01 20:08:54 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -30,6 +30,7 @@
 package Apache::lonclonecourse;
 use LONCAPA;
 use Apache::lonnet;
+use Apache::lonlocal;
 use DateTime();
 use DateTime::TimeZone;
 
@@ -130,10 +131,14 @@
 # =============================================================== Copy a dbfile
 
 sub copydb {
-    my ($origcrsid,$newcrsid,$which,$newinstcode)=@_;
+    my ($origcrsid,$newcrsid,$which,$newinstcode,$newowner,$tinyurls)=@_;
     $which=~s/\.db$//;
     my %origcrsdata=&Apache::lonnet::coursedescription($origcrsid);
     my %newcrsdata= &Apache::lonnet::coursedescription($newcrsid);
+    if (($which eq 'tiny') && ($tinyurls eq 'delete')) {
+        return ();
+    }
+    my @info;
     my %data=&Apache::lonnet::dump
 	($which,$origcrsdata{'domain'},$origcrsdata{'num'});
     foreach my $key (keys(%data)) {
@@ -146,9 +151,89 @@
         if ($origcrsdata{'internal.coursecode'} ne $newinstcode) {
             $data{'crslabel'} =~ s/\Q$origcrsdata{'internal.coursecode'}\E/$newinstcode/;
         }
+    } elsif ($which eq 'tiny') {
+        my $oldprefix = 'uploaded/'.$origcrsdata{'domain'}.'/'.$origcrsdata{'num'}.'/';
+        my $newprefix = 'uploaded/'.$newcrsdata{'domain'}.'/'. $newcrsdata{'num'}.'/';
+        my (%domtiny,%tocreate, at todelete,$numnew,$errors);
+        if (($tinyurls eq 'transfer') && (keys(%data))) {
+            unless (($origcrsdata{'internal.courseowner'} eq $newowner) &&
+                    ($origcrsdata{'domain'} eq $newcrsdata{'domain'})) {
+                $tinyurls = 'create';
+                push(@info,{
+                             mt => "Action for URL shortcut(s) changed from 'transfer' to 'create' ".
+                                   "because requirements of same owner and some course domain ".
+                                   "for new course and original course not met.",
+                             args => [],
+                           });
+            }
+        }
+        foreach my $key (keys(%data)) {
+            my $code = $data{$key};
+            my $newkey = $key;
+            $newkey =~ s{\Q$oldprefix\E}{$newprefix}g;
+            if ($tinyurls eq 'transfer') {
+                $data{$newkey} = $code;
+                $domtiny{$code} = $newcrsdata{'num'}.'&'.$newkey;
+                push(@todelete,$key);
+            } else {
+                $tocreate{$newcrsdata{'num'}.'&'.$newkey} = 1;
+            }
+            delete($data{$key});
+        }
+        if (keys(%tocreate)) {
+            ($numnew,$errors) = &Apache::loncommon::make_short_symbs($newcrsdata{'domain'},
+                                                                     $newcrsdata{'num'},
+                                                                     \%tocreate,$newowner);
+            if ((ref($errors) eq 'ARRAY') && (@{$errors} > 0)) {
+                push(@info,{
+                            mt => 'Error(s) when creating URL shortcut(s) in new course for equivalent '.
+                                  'resource(s)/folder(s) in original course: [_1]',
+                            args => [join(', ',@{$errors})],
+                           });
+            }
+            if ($numnew) {
+                push(@info,{
+                            mt => 'New URL shortcut(s) in new course for [quant,_1,item] to replicate '.
+                                  'shortcut(s) for equivalent(s) in original course.',
+                            args => [$numnew],
+                           });
+            }
+            return @info;
+        } elsif (keys(%domtiny)) {
+            my $configuname = &Apache::lonnet::get_domainconfiguser($newcrsdata{'domain'});
+            my $putdomres = &Apache::lonnet::put('tiny',\%domtiny,$newcrsdata{'domain'},$configuname);
+            if ($putdomres eq 'ok') {
+                my $delres = &Apache::lonnet::del('tiny',\@todelete,
+                                                 $origcrsdata{'domain'},
+                                                 $origcrsdata{'num'});
+
+                if ($delres eq 'ok') {
+                    push(@info,{
+                                 mt => 'URL shortcut(s) for [quant,_1,item] transferred, and '.
+                                       'now point to resource(s)/folder(s) in new course instead of '.
+                                       'equivalent(s) in original course.',
+                                 args => [scalar(keys(%domtiny))],
+                               });
+                } else {
+                    push(@info,{
+                                 mt => 'Failed to delete URL shortcut(s) in original course '.
+                                       'when attempting to transfer to new course.',
+                                 args => [],
+                               });
+                }
+            } else {
+                push(@info,{
+                              mt => 'Failed to store update of target course for URL shortcut(s) in '.
+                                    'domain records.',
+                              args => [],
+                           });
+                return @info;
+            }
+        }
     }
-    return &Apache::lonnet::put
-	($which,\%data,$newcrsdata{'domain'},$newcrsdata{'num'});
+    my $putres = &Apache::lonnet::put
+                     ($which,\%data,$newcrsdata{'domain'},$newcrsdata{'num'});
+    return @info;
 }
 
 # ========================================================== Copy resourcesdata
@@ -259,32 +344,41 @@
 	    &copyfile($origcrsid,$newcrsid,$_);
 	}
     }
+    return;
 }
 # ========================================================== Copy all userfiles
 
 sub copydbfiles {
-    my ($origcrsid,$newcrsid,$newinstcode)=@_;
+    my ($origcrsid,$newcrsid,$newinstcode,$newowner,$tinyurls)=@_;
+    my @copyinfo;
 
     my ($origcrs_discussion) = ($origcrsid=~m|^/(.*)|);
     $origcrs_discussion=~s|/|_|g;
     foreach (&crsdirlist($origcrsid)) {
-	if ($_=~/\.db$/) {
-        unless ($_=~/^(nohist\_|disclikes|discussiontimes|classlist|versionupdate
-                |resourcedata|\Q$origcrs_discussion\E|slots|slot_reservations
-                |gradingqueue|reviewqueue|CODEs|groupmembership|comm_block)/) {
-            &copydb($origcrsid,$newcrsid,$_,$newinstcode);
+        if ($_=~/\.db$/) {
+            unless ($_=~/^(nohist\_|disclikes|discussiontimes|classlist|versionupdate
+                   |resourcedata|\Q$origcrs_discussion\E|slots|slot_reservations
+                   |gradingqueue|reviewqueue|CODEs|groupmembership|comm_block)/) {
+                my @info = &copydb($origcrsid,$newcrsid,$_,$newinstcode,$newowner,
+                                   $tinyurls);
+                if (@info) {
+                    push(@copyinfo, at info);
+                }
+            }
         }
-	}
     }
+    return @copyinfo;
 }
 
 # ======================================================= Copy all course files
 
 sub copycoursefiles {
-    my ($origcrsid,$newcrsid,$date_mode,$date_shift,$newinstcode)=@_;
+    my ($origcrsid,$newcrsid,$date_mode,$date_shift,$newinstcode,$newowner,
+        $tinyurls)=@_;
     &copyuserfiles($origcrsid,$newcrsid);
-    &copydbfiles($origcrsid,$newcrsid,$newinstcode);
+    my @info = &copydbfiles($origcrsid,$newcrsid,$newinstcode,$newowner,$tinyurls);
     &copyresourcedb($origcrsid,$newcrsid,$date_mode,$date_shift);
+    return @info;
 }
 
 1;
Index: loncom/interface/loncommon.pm
diff -u loncom/interface/loncommon.pm:1.1343 loncom/interface/loncommon.pm:1.1344
--- loncom/interface/loncommon.pm:1.1343	Tue Jun  9 21:32:32 2020
+++ loncom/interface/loncommon.pm	Wed Jul  1 20:08:54 2020
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.1343 2020/06/09 21:32:32 raeburn Exp $
+# $Id: loncommon.pm,v 1.1344 2020/07/01 20:08:54 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -15868,7 +15868,8 @@
     my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
     my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
     my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
-    my $clonemsg;
+    my $clonetitle;
+    my @clonemsg;
     my $can_clone = 0;
     my $lctype = lc($args->{'crstype'});
     if ($lctype ne 'community') {
@@ -15876,16 +15877,38 @@
     }
     if ($clonehome eq 'no_host') {
         if ($args->{'crstype'} eq 'Community') {
-            $clonemsg = &mt('No new community created.').$linefeed.&mt('A new community could not be cloned from the specified original - [_1] - because it is a non-existent community.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
+            push(@clonemsg,({
+                              mt => 'No new community created.',
+                              args => [],
+                            },
+                            {
+                              mt => 'A new community could not be cloned from the specified original - [_1] - because it is a non-existent community.',
+                              args => [$args->{'clonedomain'}.':'.$args->{'clonedomain'}],
+                            }));
         } else {
-            $clonemsg = &mt('No new course created.').$linefeed.&mt('A new course could not be cloned from the specified original - [_1] - because it is a non-existent course.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
-        }     
+            push(@clonemsg,({
+                              mt => 'No new course created.',
+                              args => [],
+                            },
+                            {
+                              mt => 'A new course could not be cloned from the specified original - [_1] - because it is a non-existent course.',
+                              args => [$args->{'clonecourse'}.':'.$args->{'clonedomain'}],
+                            }));
+        }
     } else {
 	my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
+        $clonetitle = $clonedesc{'description'};
         if ($args->{'crstype'} eq 'Community') {
             if ($clonedesc{'type'} ne 'Community') {
-                $clonemsg = &mt('No new community created.').$linefeed.&mt('A new community could not be cloned from the specified original - [_1] - because it is a course not a community.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
-                return ($can_clone, $clonemsg, $cloneid, $clonehome);
+                push(@clonemsg,({
+                                  mt => 'No new community created.',
+                                  args => [],
+                                },
+                                {
+                                  mt => 'A new community could not be cloned from the specified original - [_1] - because it is a course not a community.',
+                                  args => [$args->{'clonecourse'}.':'.$args->{'clonedomain'}],
+                                }));
+                return ($can_clone,\@clonemsg,$cloneid,$clonehome);
             }
         }
 	if (($env{'request.role.domain'} eq $args->{'clonedomain'}) &&
@@ -15974,20 +15997,34 @@
             }
             unless ($can_clone) {
                 if ($args->{'crstype'} eq 'Community') {
-                    $clonemsg = &mt('No new community created.').$linefeed.&mt('The new community could not be cloned from the existing community because the new community owner ([_1]) does not have cloning rights in the existing community ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
+                    push(@clonemsg,({
+                                      mt => 'No new community created.',
+                                      args => [],
+                                    },
+                                    {
+                                      mt => 'The new community could not be cloned from the existing community because the new community owner ([_1]) does not have cloning rights in the existing community ([_2]).',
+                                      args => [$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'}],
+                                    }));
                 } else {
-                    $clonemsg = &mt('No new course created.').$linefeed.&mt('The new course could not be cloned from the existing course because the new course owner ([_1]) does not have cloning rights in the existing course ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
+                    push(@clonemsg,({
+                                      mt => 'No new course created.',
+                                      args => [],
+                                    },
+                                    {
+                                      mt => 'The new course could not be cloned from the existing course because the new course owner ([_1]) does not have cloning rights in the existing course ([_2]).',
+                                      args => [$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'}],
+                                    }));
                 }
 	    }
         }
     }
-    return ($can_clone, $clonemsg, $cloneid, $clonehome);
+    return ($can_clone,\@clonemsg,$cloneid,$clonehome,$clonetitle);
 }
 
 sub construct_course {
     my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context,
-        $cnum,$category,$coderef) = @_;
-    my $outcome;
+        $cnum,$category,$coderef,$callercontext,$user_lh) = @_;
+    my ($outcome,$msgref,$clonemsgref);
     my $linefeed =  '<br />'."\n";
     if ($context eq 'auto') {
         $linefeed = "\n";
@@ -15996,18 +16033,11 @@
 #
 # Are we cloning?
 #
-    my ($can_clone, $clonemsg, $cloneid, $clonehome);
+    my ($can_clone,$cloneid,$clonehome,$clonetitle);
     if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
-	($can_clone, $clonemsg, $cloneid, $clonehome) = &check_clone($args,$linefeed);
-	if ($context ne 'auto') {
-            if ($clonemsg ne '') {
-	        $clonemsg = '<span class="LC_error">'.$clonemsg.'</span>';
-            }
-	}
-	$outcome .= $clonemsg.$linefeed;
-
+	($can_clone,$clonemsgref,$cloneid,$clonehome,$clonetitle) = &check_clone($args,$linefeed);
         if (!$can_clone) {
-	    return (0,$outcome);
+	    return (0,$outcome,$clonemsgref);
 	}
     }
 
@@ -16030,15 +16060,20 @@
                                              $args->{'ccuname'}.':'.
                                              $args->{'ccdomain'},
                                              $args->{'crstype'},
-                                             $cnum,$context,$category);
+                                             $cnum,$context,$category,
+                                             $callercontext);
 
     # Note: The testing routines depend on this being output; see 
     # Utils::Course. This needs to at least be output as a comment
     # if anyone ever decides to not show this, and Utils::Course::new
     # will need to be suitably modified.
-    $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$showncrstype,$$courseid).$linefeed;
+    if (($callercontext eq 'auto') && ($user_lh ne '')) {
+        $outcome .= &mt_user($user_lh,'New LON-CAPA [_1] ID: [_2]',$showncrstype,$$courseid).$linefeed;
+    } else {
+        $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$showncrstype,$$courseid).$linefeed;
+    }
     if ($$courseid =~ /^error:/) {
-        return (0,$outcome);
+        return (0,$outcome,$clonemsgref);
     }
 
 #
@@ -16047,24 +16082,37 @@
     ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
     my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
     if ($crsuhome eq 'no_host') {
-        $outcome .= &mt('Course creation failed, unrecognized course home server.').$linefeed;
-        return (0,$outcome);
+        if (($callercontext eq 'auto') && ($user_lh ne '')) {
+            $outcome .= &mt_user($user_lh,
+                            'Course creation failed, unrecognized course home server.');
+        } else {
+            $outcome .= &mt('Course creation failed, unrecognized course home server.');
+        }
+        $outcome .= $linefeed;
+        return (0,$outcome,$clonemsgref);
     }
     $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
 
 #
 # Do the cloning
 #   
+    my @clonemsg;
     if ($can_clone && $cloneid) {
-	$clonemsg = &mt('Cloning [_1] from [_2]',$showncrstype,$clonehome);
-	if ($context ne 'auto') {
-	    $clonemsg = '<span class="LC_success">'.$clonemsg.'</span>';
-	}
-	$outcome .= $clonemsg.$linefeed;
+        push(@clonemsg,
+                      {
+                          mt => 'Created [_1] by cloning from [_2]',
+                          args => [$showncrstype,$clonetitle],
+                      });
 	my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
 # Copy all files
-	&Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},
-	                                         $args->{'dateshift'},$args->{'crscode'});
+        my @info =
+	    &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},
+	                                             $args->{'dateshift'},$args->{'crscode'},
+                                                     $args->{'ccuname'}.':'.$args->{'ccdomain'},
+                                                     $args->{'tinyurls'});
+        if (@info) {
+            push(@clonemsg, at info);
+        }
 # Restore URL
 	$cenv{'url'}=$oldcenv{'url'};
 # Restore title
@@ -16395,7 +16443,7 @@
                  ('resourcedata',\%storecontent,$$crsudom,$$crsunum); 
     }
 
-    return (1,$outcome);
+    return (1,$outcome,\@clonemsg);
 }
 
 sub make_unique_code {
@@ -18201,24 +18249,37 @@
     return $plaintext;
 }
 
-sub make_short_symbs {
+sub get_requested_shorturls {
     my ($cdom,$cnum,$navmap) = @_;
     return unless (ref($navmap));
-    my ($numnew, at errors);
+    my ($numnew,$errors);
     my @toshorten = &Apache::loncommon::get_env_multiple('form.addtiny');
     if (@toshorten) {
         my (%maps,%resources,%titles);
         &Apache::loncourserespicker::enumerate_course_contents($navmap,\%maps,\%resources,\%titles,
                                                                'shorturls',$cdom,$cnum);
-        my %tocreate;
         if (keys(%resources)) {
+            my %tocreate;
             foreach my $item (sort {$a <=> $b} (@toshorten)) {
                 my $symb = $resources{$item};
                 if ($symb) {
                     $tocreate{$cnum.'&'.$symb} = 1;
                 }
             }
+            if (keys(%tocreate)) {
+                ($numnew,$errors) = &make_short_symbs($cdom,$cnum,
+                                                      \%tocreate);
+            }
         }
+    }
+    return ($numnew,$errors);
+}
+
+sub make_short_symbs {
+    my ($cdom,$cnum,$tocreateref,$lockuser) = @_;
+    my ($numnew, at errors);
+    if (ref($tocreateref) eq 'HASH') {
+        my %tocreate = %{$tocreateref};
         if (keys(%tocreate)) {
             my %coursetiny = &Apache::lonnet::dump('tiny',$cdom,$cnum);
             my $su = Short::URL->new(no_vowels => 1);
@@ -18226,9 +18287,11 @@
             my (%newunique,%addcourse,%courseonly,%failed);
             # get lock on tiny db
             my $now = time;
+            if ($lockuser eq '') {
+                $lockuser = $env{'user.name'}.':'.$env{'user.domain'};
+            }
             my $lockhash = {
-                                "lock\0$now" => $env{'user.name'}.
-                                                ':'.$env{'user.domain'},
+                                "lock\0$now" => $lockuser,
                             };
             my $tries = 0;
             my $gotlock = &Apache::lonnet::newput_dom('tiny',$lockhash,$cdom);
Index: loncom/interface/loncoursequeueadmin.pm
diff -u loncom/interface/loncoursequeueadmin.pm:1.58 loncom/interface/loncoursequeueadmin.pm:1.59
--- loncom/interface/loncoursequeueadmin.pm:1.58	Thu Aug  3 16:28:39 2017
+++ loncom/interface/loncoursequeueadmin.pm	Wed Jul  1 20:08:54 2020
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Utilities to administer domain course requests and course self-enroll requests
 #
-# $Id: loncoursequeueadmin.pm,v 1.58 2017/08/03 16:28:39 raeburn Exp $
+# $Id: loncoursequeueadmin.pm,v 1.59 2020/07/01 20:08:54 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -304,11 +304,11 @@
     my $stamp = time;
     my $msgcount = &Apache::lonmsg::get_uniq();
     my $sender_lh = &Apache::loncommon::user_lang($uname,$udom,$cid);
-    $subject = &Apache::lonlocal::mt_user($sender_lh,$rawsubj);
+    $subject = &mt_user($sender_lh,$rawsubj);
     $message = '';
     foreach my $item (@rawmsg) {
         if (ref($item) eq 'HASH') {
-            $message .= &Apache::lonlocal::mt_user($sender_lh,$item->{mt},@{$item->{args}})."\n";
+            $message .= &mt_user($sender_lh,$item->{mt},@{$item->{args}})."\n";
         }
     }
     &Apache::lonmsg::process_sent_mail($subject,'',$numsent,$stamp,$uname,$udom,$msgcount,$cid,$$,$message,
@@ -322,41 +322,41 @@
     foreach my $recip (sort(keys(%{$msgcc}))) {
         my ($ccname,$ccdom) = split(/:/,$recip);
         my $recip_lh = &Apache::loncommon::user_lang($ccname,$ccdom,$cid);
-        my $subject = &Apache::lonlocal::mt_user($sender_lh,$rawsubj);
+        my $subject = &mt_user($sender_lh,$rawsubj);
         my $message = '';
         foreach my $item (@rawmsg) {
             if (ref($item) eq 'HASH') {
-                $message .= &Apache::lonlocal::mt_user($sender_lh,$item->{mt},
-                                                       @{$item->{args}})."\n";
+                $message .= &mt_user($sender_lh,$item->{mt},
+                                     @{$item->{args}})."\n";
             }
         }
         if ($context eq 'coursemanagers') {
             if ($approvedlist) {
-                $message .= "\n\n".&Apache::lonlocal::mt_user($sender_lh,'Approved enrollments:')."\n".$approvedlist;
+                $message .= "\n\n".&mt_user($sender_lh,'Approved enrollments:')."\n".$approvedlist;
             }
             if ($rejectedlist) {
-                $message .= "\n\n".&Apache::lonlocal::mt_user($sender_lh,'Rejected enrollments:')."\n".$rejectedlist;
+                $message .= "\n\n".&mt_user($sender_lh,'Rejected enrollments:')."\n".$rejectedlist;
             }
         } elsif ($context eq 'domainmanagers') {
             if ($approvedlist) {
-                $message .= "\n\n".&Apache::lonlocal::mt_user($sender_lh,'Approved course requests:')."\n".$approvedlist;
+                $message .= "\n\n".&mt_user($sender_lh,'Approved course requests:')."\n".$approvedlist;
             }
             if ($rejectedlist) {
-                $message .= "\n\n".&Apache::lonlocal::mt_user($sender_lh,'Rejected course requests:')."\n".$rejectedlist;
+                $message .= "\n\n".&mt_user($sender_lh,'Rejected course requests:')."\n".$rejectedlist;
             }
         } elsif ($context eq 'authormanagers') {
             if ($approvedlist) {
-                $message .= "\n\n".&Apache::lonlocal::mt_user($sender_lh,'Approved author role requests:')."\n".$approvedlist;
+                $message .= "\n\n".&mt_user($sender_lh,'Approved author role requests:')."\n".$approvedlist;
             }
             if ($rejectedlist) {
-                $message .= "\n\n".&Apache::lonlocal::mt_user($sender_lh,'Rejected author role requests:')."\n".$rejectedlist;
+                $message .= "\n\n".&mt_user($sender_lh,'Rejected author role requests:')."\n".$rejectedlist;
             }
         } elsif ($context eq 'usernamemanagers') {
             if ($approvedlist) {
-                $message .= "\n\n".&Apache::lonlocal::mt_user($sender_lh,'Approved LON-CAPA account requests:')."\n".$approvedlist;
+                $message .= "\n\n".&mt_user($sender_lh,'Approved LON-CAPA account requests:')."\n".$approvedlist;
             }
             if ($rejectedlist) {
-                $message .= "\n\n".&Apache::lonlocal::mt_user($sender_lh,'Rejected LON-CAPA account requests:')."\n".$rejectedlist;
+                $message .= "\n\n".&mt_user($sender_lh,'Rejected LON-CAPA account requests:')."\n".$rejectedlist;
             }
         }
         $status .= &Apache::lonmsg::user_normal_msg($ccname,$ccdom,$subject,$message,undef,undef,undef,1,
@@ -982,7 +982,9 @@
                                                      $ownerdom,$ownername);
                         if ((ref($history{'details'}) eq 'HASH') && 
                             ($history{'disposition'} eq $queue)) {
-                            my ($logmsg,$newusermsg,$addresult,$enrollcount,$response,$keysmsg,$code,%customitems);
+                            my ($logmsg,$newusermsg,$addresult,$enrollcount,$response,
+                                $keysmsg,$code,%customitems);
+                            my $clonemsg = [];
                             my $fullname = '';
                             my $inprocess = &Apache::lonnet::auto_crsreq_update($cdom,$cnum,$crstype,'process',$ownername,
                                                                                 $ownerdom,$fullname,$coursedesc);
@@ -996,8 +998,8 @@
                             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,
+                            my ($result,$postprocess) = &course_creation($cdom,$cnum,$context,$history{'details'},
+                                                        \$logmsg,$clonemsg,\$newusermsg,\$addresult,\$enrollcount,
                                                         \$response,\$keysmsg,\%domdefs,$longroles,\$code,\%customitems);
                             if ($result eq 'created') {
                                 if ($crstype eq 'community') {
@@ -1010,6 +1012,9 @@
                                     if (ref($approvedmsg->[1]) eq 'HASH') {
                                         $approvedmsg->[1]->{'args'} = [$firsturl];
                                     }
+                                    if ((ref($clonemsg) eq 'ARRAY') && (@{$clonemsg})) {
+                                        push(@{$approvedmsg},@{$clonemsg});
+                                    }
                                     if ($code) {
                                         push(@{$approvedmsg},
                                             {
@@ -1608,8 +1613,9 @@
 }
 
 sub course_creation {
-    my ($dom,$cnum,$context,$details,$logmsg,$newusermsg,$addresult,$enrollcount,$output,
-        $keysmsg,$domdefs,$longroles,$coderef,$customhash) =  @_;
+    my ($dom,$cnum,$context,$details,$logmsg,$clonemsg,$newusermsg,$addresult,
+        $enrollcount,$output,$keysmsg,$domdefs,$longroles,$coderef,$customhash,
+        $callercontext,$user_lh) = @_;
     unless ((ref($details) eq 'HASH') && (ref($domdefs) eq 'HASH') && 
             (ref($longroles) eq 'HASH')) {
         return ('error: Invalid request');
@@ -1643,8 +1649,9 @@
     }
     my %reqdetails = &build_batchcreatehash($dom,$context,$details,$owneremail,$domdefs);
     my $cid = &LONCAPA::batchcreatecourse::build_course($dom,$cnum,'requestcourses',
-                  \%reqdetails,$longroles,$logmsg,$newusermsg,$addresult,
-                  $enrollcount,$output,$keysmsg,$ownerdom,$ownername,$cnum,$crstype,$coderef);
+                  \%reqdetails,$longroles,$logmsg,$clonemsg,$newusermsg,$addresult,
+                  $enrollcount,$output,$keysmsg,$ownerdom,$ownername,$cnum,$crstype,
+                  $coderef,$callercontext,$user_lh);
     my $postprocess;
     if ($cid eq "/$dom/$cnum") {
         $result = 'created';
@@ -1664,7 +1671,7 @@
 sub build_batchcreatehash {
     my ($dom,$context,$details,$owneremail,$domdefs) = @_;
     my %batchhash;
-    my @items = qw{owner domain coursehome clonecrs clonedom datemode dateshift enrollstart enrollend accessstart accessend sections crosslists users uniquecode};
+    my @items = qw{owner domain coursehome clonecrs clonedom datemode dateshift tinyurls enrollstart enrollend accessstart accessend sections crosslists users uniquecode};
     if ((ref($details) eq 'HASH') && (ref($domdefs) eq 'HASH')) {
         my $emailenc = &escape($owneremail);
         my $owner = $details->{'owner'}.':'.$details->{'domain'};
@@ -2017,9 +2024,10 @@
         $longroles{$role}=&Apache::lonnet::plaintext($role);
     }
     my %domdefs = &Apache::lonnet::get_domain_defaults($dom);
-    my ($output,$linefeed);
+    my ($output,$linefeed,$user_lh);
     if ($context eq 'auto') {
         $linefeed = "\n";
+        $user_lh = &Apache::loncommon::user_lang($dcname,$dcdom);
     } else {
         $linefeed = '<br />'."\n";
     }
@@ -2081,6 +2089,7 @@
                 $reqstatus = $disposition;
                 if ($disposition eq 'process') {
                     my ($logmsg,$newusermsg,$addresult,$enrollcount,$response,$keysmsg,$code);
+                    my $clonemsg = [];
                     my %customitems;
                     my $fullname = &Apache::loncommon::plainname($ownername,$ownerdom);
                     my $inprocess = &Apache::lonnet::auto_crsreq_update($dom,$cnum,$crstype,'process',$ownername,
@@ -2096,8 +2105,9 @@
                         $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);
+                        &course_creation($dom,$cnum,'domain',$history{'details'},\$logmsg,$clonemsg,\$newusermsg,
+                                         \$addresult,\$enrollcount,\$response,\$keysmsg,\%domdefs,\%longroles,
+                                         \$code,\%customitems,$context,$user_lh);
                     if ($result eq 'created') {
                         $disposition = 'created';
                         $reqstatus = 'created';
@@ -2112,14 +2122,20 @@
                                 [{
                                     mt => 'Your requested course: [_1], (queued pending validation) has now been created.',
                                     args => [$cdescr],
-                                 },
+                                 }];
+                            if ((ref($clonemsg) eq 'ARRAY') && (@{$clonemsg})) {
+                                push(@{$approvedmsg},@{$clonemsg});
+                            }
+                            push(@{$approvedmsg},
                                  {
                                     mt   => 'Visit [_1] to log-in and access the course.',
                                     args => [$firsturl],
                                  },
                                  {
-                                    mt => 'If currently logged-in to LON-CAPA, log-out and log-in again to select your new course role.'
-                                 }];
+                                    mt => 'If currently logged-in to LON-CAPA, log-out and log-in again to select your new course role.',
+                                    args => [],
+                                 }
+                            );
                             my $sender = $dcname.':'.$dcdom;
                             if (ref($postprocess) eq 'HASH') {
                                 if (ref($postprocess->{'createdmsg'}) eq 'ARRAY') {
Index: loncom/interface/loncreatecourse.pm
diff -u loncom/interface/loncreatecourse.pm:1.174 loncom/interface/loncreatecourse.pm:1.175
--- loncom/interface/loncreatecourse.pm:1.174	Fri May 22 19:35:08 2020
+++ loncom/interface/loncreatecourse.pm	Wed Jul  1 20:08:54 2020
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Create a course
 #
-# $Id: loncreatecourse.pm,v 1.174 2020/05/22 19:35:08 raeburn Exp $
+# $Id: loncreatecourse.pm,v 1.175 2020/07/01 20:08:54 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -80,7 +80,6 @@
 use Apache::londocs;
 use Apache::lonlocal;
 use Apache::lonuserutils;
-use Apache::lonclonecourse;
 use LONCAPA::batchcreatecourse;
 use LONCAPA qw(:DEFAULT :match);
 
@@ -192,6 +191,10 @@
                     'ncd'  => "Do not clone date parameters",
                     'prd'  => 'Clone date parameters as-is',
                     'shd'  => 'Shift date parameters by number of days',
+                    'dpl'  => 'URL shortcuts (for deep linking)',
+                    'nsl'  => 'Do not clone URL shortcuts',
+                    'tsl'  => 'Transfer URL shortcuts from existing course to new course',
+                    'csl'  => 'Create new URL shortcuts in new course',
                     'assp' => "Assessment Parameters",
                     'oaas' => "Open all assessments",
                     'sta'  => "starting from:",
@@ -257,6 +260,8 @@
         $lt{'cc'}  = &mt('(will be assigned Coordinator role)');
         $lt{'cgrs'} = &mt('Community Group Settings');
         $lt{'cgrq'} = &mt('Set a quota for the total disk space available for storage of community group portfolio files');
+        $lt{'tsl'} = &mt('Transfer URL shortcuts from existing community to new community');
+        $lt{'csl'} = &mt('Create new URL shortcuts in new community');
     } elsif ($crstype eq 'Placement') {
         $lt{'cinf'} = &mt('Placement Test Information');
         $lt{'ctit'} = &mt('Placement Test Title');
@@ -430,6 +435,7 @@
                  .'<input type="radio" name="firstres" value="nav" />'.$lt{'navi'}
                  .'</label>');
     }
+    my $tinyurlhelp=&Apache::loncommon::help_open_topic('Clone_Tiny_URLs');
     $r->print('<br />('.$lt{'stco'}.')'
                  .&Apache::lonhtmlcommon::row_closure(1)
                  .&Apache::lonhtmlcommon::end_pick_box()
@@ -461,6 +467,17 @@
                  .'</label>'
                  .' <input type="text" size="5" name="dateshift" value="365" />'
                  .&Apache::lonhtmlcommon::row_closure()
+                 .&Apache::lonhtmlcommon::row_title($tinyurlhelp.$lt{'dpl'})
+                 .'<label>'
+                 .'<input type="radio" name="tinyurls" value="delete" /> '.$lt{'nsl'}
+                 .'</label><br />'
+                 .'<label>'
+                 .'<input type="radio" name="tinyurls" value="transfer" /> '.$lt{'tsl'}
+                 .'</label><br />'
+                 .'<label>'
+                 .'<input type="radio" name="tinyurls" value="create" checked="checked" /> '.$lt{'csl'}
+                 .'</label>'
+                 .&Apache::lonhtmlcommon::row_closure()
                  .&Apache::lonhtmlcommon::row_headline()
                  .'<span class="LC_info">'.$lt{'asov'}.'</span>'
                  .&Apache::lonhtmlcommon::row_closure(1)
@@ -686,6 +703,7 @@
                clonedomain => $env{'form.clonedomain'},
                datemode => $env{'form.datemode'},
                dateshift => $env{'form.dateshift'},
+               tinyurls  => $env{'form.tinyurls'},
                crsid => $env{'form.crsid'},
                curruser => $env{'user.name'}.':'.$env{'user.domain'},
                crssections => $env{'form.crssections'},
@@ -753,12 +771,20 @@
         return;
     }
     my ($courseid,$crsudom,$crsunum,$code);
-    my ($success,$output) = 
+    my ($success,$output,$clonemsgref) = 
 	&Apache::loncommon::construct_course($args,\$logmsg,\$courseid,
 					     \$crsudom,\$crsunum,
 					     $env{'user.domain'},
 					     $env{'user.name'},'dc_create',undef,undef,\$code);
     $r->print($output);
+    if (ref($clonemsgref) eq 'ARRAY') {
+        my $user_lh = &Apache::loncommon::user_lang($env{'user.name'},$env{'user.domain'});
+        foreach my $item (@{$clonemsgref}) {
+            if (ref($item) eq 'HASH') {
+                $r->print(&mt($item->{mt},@{$item->{args}}).'<br />'."\n");
+            }
+        }
+    }
     if ($success) {
         #
 	# Make the requested user a course coordinator or group coordinator
@@ -913,7 +939,7 @@
     my $uname = $env{'user.name'};
     my $udom = $env{'user.domain'};
     my $dir = &LONCAPA::tempdir().'addcourse';
-    my ($result,$logmsg,$keysmsg,$codesref,$instcodesref);
+    my ($result,$logmsg,$clonemsg,$keysmsg,$codesref,$instcodesref);
     if (($defdom =~ /^$match_domain$/) && ($uname =~ /^$match_username$/) && ($udom =~/^$match_domain$/)) {
         my $batchfilepath=&Apache::lonnet::userfileupload('coursecreatorxml',undef,
                                                           'batchupload',undef,undef,
@@ -927,7 +953,7 @@
                     if ((defined($filename)) && (defined($batchdir))) {
                         my @requests = ($filename);
                         my %courseids = ();
-                        ($result,$logmsg,$keysmsg,$codesref,$instcodesref) =
+                        ($result,$logmsg,$clonemsg,$keysmsg,$codesref,$instcodesref) =
                             &LONCAPA::batchcreatecourse::create_courses(
                                        \@requests,\%courseids,'web',$defdom,
                                        $uname,$udom);
@@ -949,7 +975,10 @@
                                 &Apache::lonnet::devalidate_cache_new('instcats',$defdom);
                                 $updatecats = 1;
                             } 
-                            &register_cleanups($r,$defdom,$updatecats); 
+                            &register_cleanups($r,$defdom,$updatecats);
+                        }
+                        if ($clonemsg) {
+                            $clonemsg = '<p class="LC_info">'.$clonemsg.'</p>'."\n";
                         }
                     }
                 } else {
@@ -967,7 +996,7 @@
     }
     $r->print(&Apache::loncommon::start_page('Create a New Course, Community or Placement Test').
               &Apache::lonhtmlcommon::breadcrumbs('Creation Outcome','Create_Course',undef,'Create_Courses').
-              $logmsg.$result.'<br /><a href="/adm/createcourse">'.
+              $logmsg.$clonemsg.$result.'<br /><a href="/adm/createcourse">'.
               &mt('Creation options menu').'</a>'.
               &Apache::loncommon::end_page());
  
@@ -1556,7 +1585,9 @@
            my $start_page=&Apache::loncommon::start_page('Requests Validation Result',$js);
            my $crumbs = &Apache::lonhtmlcommon::breadcrumbs('Validation Attempted','Course_Requests',undef,'Course_Requests');
            $r->print($start_page.$crumbs."\n".'<div>'.
-                     &Apache::loncoursequeueadmin::process_official_reqs('domain',$env{'request.role.domain'}).'</div>'.
+                     &Apache::loncoursequeueadmin::process_official_reqs('domain',$env{'request.role.domain'},
+                                                                         $env{'user.name'},$env{'user.domain'}).
+                     '</div>'.
                      &Apache::loncommon::end_page());
        } elsif (($env{'form.phase'} eq 'creationlog') && ($show_all_choices)) {
            &Apache::lonhtmlcommon::add_breadcrumb
Index: loncom/interface/lonrequestcourse.pm
diff -u loncom/interface/lonrequestcourse.pm:1.108 loncom/interface/lonrequestcourse.pm:1.109
--- loncom/interface/lonrequestcourse.pm:1.108	Wed Feb 12 22:15:40 2020
+++ loncom/interface/lonrequestcourse.pm	Wed Jul  1 20:08:54 2020
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Request a course
 #
-# $Id: lonrequestcourse.pm,v 1.108 2020/02/12 22:15:40 raeburn Exp $
+# $Id: lonrequestcourse.pm,v 1.109 2020/07/01 20:08:54 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -577,6 +577,7 @@
                 clonedom         => 'selectbox',
                 datemode         => 'radio',
                 dateshift        => 'text',
+                tinyurls         => 'radio',
             },
             enrollment  => {
                 accessstart_month  => 'selectbox',
@@ -2956,7 +2957,7 @@
         $inst_values .= '<td>'.$env{'form.coursecredits'}.'</td>';
     }
 
-    my %ctxt = &clone_text();
+    my %ctxt = &clone_text($env{'form.crstype'});
     $inst_headers .= '<th>'.&mt('Clone From').'</th>';
     if (($env{'form.cloning'}) &&
         ($env{'form.clonecrs'} =~ /^$match_name$/) && 
@@ -2968,7 +2969,8 @@
             my %courseenv = &Apache::lonnet::userenvironment($env{'form.clonedom'},
                               $env{'form.clonecrs'},('description','internal.coursecode'));
             if (keys(%courseenv) > 0) {
-                $inst_headers .= '<th>'.$ctxt{'dsh'}.'</th>';
+                $inst_headers .= '<th>'.$ctxt{'dsh'}.'</th>'.
+                                 '<th>'.$ctxt{'dpl'}.'</th>';
                 $inst_values .= '<td>'.$courseenv{'description'}.' ';
                 my $cloneinst = $courseenv{'internal.coursecode'};
                 if ($cloneinst ne '') {
@@ -2984,6 +2986,14 @@
                 } else {
                     $inst_values .= $ctxt{'ncd'};
                 }
+                $inst_values .= '</td><td>';
+                if ($env{'form.tinyurls'} eq 'delete') {
+                    $inst_values .= $ctxt{'nsl'};
+                } elsif ($env{'form.tinyurls'} eq 'transfer') {
+                    $inst_values .= $ctxt{'tsl'};
+                } else {
+                    $inst_values .= $ctxt{'csl'};
+                }
                 $inst_values .= '</td>';
              } else {
                  $inst_values .= '<td>'.&mt('Unknown').'</td>';
@@ -3215,7 +3225,7 @@
     } elsif ($crstype eq 'placement') {
         $type = 'Placement'; 
     }
-    my %lt = &clone_text();
+    my %lt = &clone_text($crstype);
     my $output .= 
         &Apache::lonhtmlcommon::row_title($lt{'dmn'}).'<label>'.
         &Apache::loncommon::select_dom_form($dom,'clonedom').'</label>'.
@@ -3233,19 +3243,37 @@
         '<input type="radio" name="datemode" value="shift" checked="checked" /> '.
         $lt{'shd'}.'</label>'.
         '<input type="text" size="5" name="dateshift" value="365" />'.
+        &Apache::lonhtmlcommon::row_closure(1).
+        &Apache::lonhtmlcommon::row_title($lt{'dpl'}).'<label>'.
+        '<input type="radio" name="tinyurls" value="delete" /> '.$lt{'nsl'}.
+        '</label><br /><label>'.
+        '<input type="radio" name="tinyurls" value="transfer" /> '.$lt{'tsl'}.
+        '</label><br /><label>'.
+        '<input type="radio" name="tinyurls" value="create" checked="checked" /> '.$lt{'csl'}.
+        '</label>'.
         &Apache::lonhtmlcommon::row_closure(1);
     return $output;
 }
 
 sub clone_text {
-    return &Apache::lonlocal::texthash(
+    my ($crstype) = @_;
+    my %lt = &Apache::lonlocal::texthash(
                'cid'  => 'Course ID',
                'dmn'  => 'Domain',
                'dsh'  => 'Date Shift',
                'ncd'  => 'Do not clone date parameters',
                'prd'  => 'Clone date parameters as-is',
                'shd'  => 'Shift date parameters by number of days',
-        );
+               'dpl'  => 'URL shortcuts (for deep linking)',
+               'nsl'  => 'Do not clone URL shortcuts',
+               'tsl'  => 'Transfer URL shortcuts from existing course to new course',
+               'csl'  => 'Create new URL shortcuts in new course',
+    );
+    if ($crstype eq 'Community') {
+        $lt{'tsl'} = &mt('Transfer URL shortcuts from existing course to new community');
+        $lt{'csl'} = &mt('Create new URL shortcuts in new course');
+    }
+    return %lt;
 }
 
 sub coursecode_form {
@@ -3692,6 +3720,7 @@
                     clonecrs       => $clonecrs,
                     datemode       => $env{'form.datemode'},
                     dateshift      => $env{'form.dateshift'},
+                    tinyurls       => $env{'form.tinyurls'},
                     sectotal       => $sectotal,
                     sections       => \%sections,
                     crosslisttotal => $crosslisttotal,
@@ -3825,7 +3854,9 @@
             $storeresult = 'rejected';
         } elsif ($disposition eq 'process') {
             my %domdefs = &Apache::lonnet::get_domain_defaults($dom);
-            my ($logmsg,$newusermsg,$addresult,$enrollcount,$response,$keysmsg,%longroles,$code);
+            my ($logmsg,$newusermsg,$addresult,$enrollcount,$response,
+                $keysmsg,%longroles,$code);
+            my $clonemsg = [];
             my $type = 'Course';
             if ($crstype eq 'community') {
                 $type = 'Community';
@@ -3858,9 +3889,9 @@
             $customitems{'_LC_coursestartdate'} = $accessstart;
             $customitems{'_LC_courseenddate'} = $accessend;
             my ($result,$postprocess) = &Apache::loncoursequeueadmin::course_creation($dom,$cnum,
-                                          'autocreate',$details,\$logmsg,\$newusermsg,\$addresult,
-                                          \$enrollcount,\$response,\$keysmsg,\%domdefs,\%longroles,
-                                          \$code,\%customitems);
+                                          'autocreate',$details,\$logmsg,$clonemsg,\$newusermsg,
+                                          \$addresult,\$enrollcount,\$response,\$keysmsg,\%domdefs,
+                                          \%longroles,\$code,\%customitems);
             if (ref($postprocess) eq 'HASH') {
                 $customized = $postprocess->{'createdcustomized'};
             }
@@ -3894,6 +3925,20 @@
                     $output .= '<br />'.$role_result;
                 }
                 $output .= '</p>';
+                if ($logmsg) {
+                    $output .= '<p>'.$logmsg.'</p>';
+                }
+                if ((ref($clonemsg) eq 'ARRAY') && (@{$clonemsg})) {
+                    $output .= '<p class="LC_info">';
+                    my $user_lh = &Apache::loncommon::user_lang($env{'user.name'},$env{'user.domain'});
+                    foreach my $item (@{$clonemsg}) {
+                        if (ref($item) eq 'HASH') {
+                            $output .= &mt_user($user_lh,$item->{mt},
+                                                @{$item->{args}}).'<br />'."\n";
+                        }
+                    }
+                    $output .= '</p>'."\n";
+                }
                 $creationresult = 'created';
                 # Flush the course logs so reverse user roles immediately updated
                 unless ($registered_flush) {
@@ -4451,6 +4496,7 @@
             }
             $env{'form.datemode'} = $reqinfo{'datemode'};
             $env{'form.dateshift'} = $reqinfo{'dateshift'};
+            $env{'form.tinyurls'} = $reqinfo{'tinyurls'};
             if ($reqinfo{'crstype'} eq 'official') {
                 $env{'form.autoadds'} = $reqinfo{'autoadds'};
                 $env{'form.autodrops'} = $reqinfo{'autodrops'};
@@ -4861,17 +4907,26 @@
 #
 # Table of user's current courses (owner and/or course coordinator)
 #
-    my %lt = &clone_text();
+    my %lt = &clone_text('Course');
     if (keys(%cloneable)) {
         $r->print('<div id="showexisting" style="display:none">'.
                   &clone_selection_table($dom,'owned',\%cloneable).
-                  '<p><input type="radio" name="owndatemode" value="delete" /> '.$lt{'ncd'}.
+                  '<fieldset style="display:inline-block"><legend>'.$lt{'dsh'}.'</legend><label>'.
+                  '<input type="radio" name="owndatemode" value="delete" /> '.$lt{'ncd'}.
                   '</label><br /><label>'.
                   '<input type="radio" name="owndatemode" value="preserve" /> '.$lt{'prd'}.
                   '</label><br /><label>'.
                   '<input type="radio" name="owndatemode" value="shift" checked="checked" /> '.
                   $lt{'shd'}.'</label>'.
                   '<input type="text" size="5" name="owndateshift" value="365" />'.
+                  '</fieldset><fieldset style="display:inline-block">'.
+                  '<legend>'.$lt{'dpl'}.'</legend><label>'.
+                  '<input type="radio" name="owntinyurls" value="delete" />'.$lt{'nsl'}.
+                  '</label><br /><label>'.
+                  '<input type="radio" name="owntinyurls" value="transfer" />'.$lt{'tsl'}.
+                  '</label><br /><label>'.
+                  '<input type="radio" name="owntinyurls" value="create" checked="checked" />'.$lt{'csl'}.
+                  '</label></fieldset>'.
                   '</div>');
     }
 #
@@ -4880,13 +4935,20 @@
     if (keys(%domcloneable)) {
         $r->print('<div id="showcolleague" style="display:none">'.
                   &clone_selection_table($dom,'colleague',\%domcloneable).
-                  '<p><input type="radio" name="colldatemode" value="delete" /> '.$lt{'ncd'}.
+                  '<fieldset style="display:inline-block"><legend>'.$lt{'dsh'}.'</legend><label>'.
+                  '<input type="radio" name="colldatemode" value="delete" /> '.$lt{'ncd'}.
                   '</label><br /><label>'.
                   '<input type="radio" name="colldatemode" value="preserve" /> '.$lt{'prd'}.
                   '</label><br /><label>'.
                   '<input type="radio" name="colldatemode" value="shift" checked="checked" /> '.
                   $lt{'shd'}.'</label>'.
                   '<input type="text" size="5" name="colldateshift" value="365" />'.
+                  '</fieldset><fieldset style="display:inline-block">'.
+                  '<legend>'.$lt{'dpl'}.'</legend><label>'.
+                  '<input type="radio" name="colltinyurls" value="delete" />'.$lt{'nsl'}.
+                  '</label><br /><label>'.
+                  '<input type="radio" name="colltinyurls" value="create" checked="checked" />'.$lt{'csl'}.
+                  '</label></fieldset>'.
                   '</div>');
     }
 
@@ -5134,6 +5196,7 @@
             } else {
                 $details->{dateshift} = '';
             }
+            $details->{tinyurls} = $env{'form.owntinyurls'};
         } elsif ($reqtype eq 'colleague') {
             $details->{datemode} = $env{'form.colldatemode'};
             if ($details->{datemode} eq 'shift') {
@@ -5141,9 +5204,11 @@
             } else {
                 $details->{dateshift} = '';
             }
+            $details->{tinyurls} = $env{'form.colltinyurls'};
         } elsif (($reqtype eq 'textbook') || ($reqtype eq 'template')) {
             $details->{datemode} = 'delete';
             $details->{dateshift} = '';
+            $details->{tinyurls} = '';
         }
         if ($details->{dateshift} ne '') {
             $details->{dateshift} =~ s/[^\d\.]+//g;
@@ -5151,6 +5216,7 @@
     } else {
         $details->{datemode} = '';
         $details->{dateshift} = '';
+        $details->{tinyurls} = '';
     }
     my $lonhost = $r->dir_config('lonHostID');
     $r->rflush();
Index: loncom/lonnet/perl/lonnet.pm
diff -u loncom/lonnet/perl/lonnet.pm:1.1422 loncom/lonnet/perl/lonnet.pm:1.1423
--- loncom/lonnet/perl/lonnet.pm:1.1422	Wed May 13 17:44:10 2020
+++ loncom/lonnet/perl/lonnet.pm	Wed Jul  1 20:08:58 2020
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.1422 2020/05/13 17:44:10 raeburn Exp $
+# $Id: lonnet.pm,v 1.1423 2020/07/01 20:08:58 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -10607,14 +10607,19 @@
 
 sub createcourse {
     my ($udom,$description,$url,$course_server,$nonstandard,$inst_code,
-        $course_owner,$crstype,$cnum,$context,$category)=@_;
+        $course_owner,$crstype,$cnum,$context,$category,$callercontext)=@_;
     $url=&declutter($url);
     my $cid='';
     if ($context eq 'requestcourses') {
         my $can_create = 0;
         my ($ownername,$ownerdom) = split(':',$course_owner);
         if ($udom eq $ownerdom) {
-            if (&usertools_access($ownername,$ownerdom,$category,undef,
+            my $reload;
+            if (($callercontext eq 'auto') &&
+               ($ownerdom eq $env{'user.domain'}) && ($ownername eq $env{'user.name'})) {
+                $reload = 'reload';
+            }
+            if (&usertools_access($ownername,$ownerdom,$category,$reload,
                                   $context)) {
                 $can_create = 1;
             }
Index: loncom/automation/batchcreatecourse.pm
diff -u loncom/automation/batchcreatecourse.pm:1.44 loncom/automation/batchcreatecourse.pm:1.45
--- loncom/automation/batchcreatecourse.pm:1.44	Fri May 22 15:05:36 2020
+++ loncom/automation/batchcreatecourse.pm	Wed Jul  1 20:09:03 2020
@@ -1,5 +1,5 @@
 #
-# $Id: batchcreatecourse.pm,v 1.44 2020/05/22 15:05:36 raeburn Exp $
+# $Id: batchcreatecourse.pm,v 1.45 2020/07/01 20:09:03 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -80,6 +80,7 @@
 # <clonedom>msu</clonedom>
 # <datemode>shift</datemode>
 # <dateshift>365</dateshift>
+# <tinyurls>create</tinyurls>
 # <showphotos></showphotos>
 # <setpolicy>1</setpolicy>
 # <setcontent>1</setcontent>
@@ -189,12 +190,16 @@
 #                   uname - username of DC who is requesting course creation
 #                   udom - domain of DC who is requesting course creation
 #  
-# outputs (four)  -  output - text recording user roles added etc.
-#                    logmsg - text to be logged
-#                    keysmsg - text containing link(s) to manage keys page(s) 
-#                    codehash - reference to hash containing courseID => unique code
-#                               where unique code is a 6 character code, to distribute
-#                               to students as a shortcut to the course.
+# outputs (six)  -  output - text recording user roles added etc.
+#                   logmsg - text to be logged
+#                   cloneinfo - text output from cloning
+#                   keysmsg - text containing link(s) to manage keys page(s) 
+#                   codehash - reference to hash containing courseID => unique code
+#                              where unique code is a 6 character code, to distribute
+#                              to students as a shortcut to the course.
+#                   instcodes - references to hash of an array, where keys are
+#                               institutional codes and values are an array of
+#                               courseIDs of courses with that code.
 #############################################################
 
 sub create_courses {
@@ -212,10 +217,12 @@
             $longroles{'Community'}{$1} = $3;
         }
     }
-    my ($logmsg,$keysmsg,$newusermsg,$addresult,%codehash,%instcodes);
+    my ($logmsg,$cloneinfo,$keysmsg,$newusermsg,$addresult,$user_lh,
+        %codehash,%instcodes);
     my %enrollcount = ();
     my $newcoursedir = LONCAPA::tempdir().'/addcourse/'.$dom.'/'.$context;
     if ($context eq 'auto') {
+        $user_lh = &Apache::loncommon::user_lang($uname,$udom);
         $newcoursedir .= '/pending';
     } else {
         if ($uname && $udom) {
@@ -227,14 +234,16 @@
     if (@{$requests} > 0) {
         foreach my $request (@{$requests}) {
             my %details = ();
+            my $clonemsg = [];
             if (-e $newcoursedir.'/'.$request) {
                 &parse_coursereqs($newcoursedir.'/'.$request, \%details);
                 foreach my $num (sort(keys(%details))) {
                     my $reqdetails = $details{$num};
                     my $code;
-                    my $courseid = 
-                        &build_course($dom,$num,$context,$reqdetails,\%longroles,\$logmsg,\$newusermsg,
-                                      \$addresult,\%enrollcount,\$output,\$keysmsg,undef,undef,undef,undef,\$code);
+                    my $courseid =
+                        &build_course($dom,$num,$context,$reqdetails,\%longroles,\$logmsg,
+                                      $clonemsg,\$newusermsg,\$addresult,\%enrollcount,
+                                      \$output,\$keysmsg,undef,undef,undef,undef,\$code);
                     if ($courseid =~m{^/$match_domain/$match_courseid}) {
                         $$courseids{$courseid} = $details{$num}{'class'};
                         if ($code) {
@@ -244,11 +253,33 @@
                             push(@{$instcodes{$details{$num}{'coursecode'}}},$courseid);
                         }
                     }
+                    if (@{$clonemsg}) {
+                        if ($context eq 'web') {
+                            $cloneinfo .= '<p class="LC_info">';
+                        }
+                        foreach my $item (@{$clonemsg}) {
+                            if (ref($item) eq 'HASH') {
+                                if ($context eq 'auto') {
+                                    $cloneinfo .= &mt_user($user_lh,$item->{mt},
+                                                           @{$item->{args}});
+                                } else {
+                                    $cloneinfo .= &mt($item->{mt},@{$item->{args}});
+                                }
+                            }
+                            if ($context eq 'web') {
+                                $cloneinfo .= '<br />';
+                            }
+                            $cloneinfo .= "\n";
+                        }
+                        if ($context eq 'web') {
+                            $cloneinfo .= '</p>';
+                        }
+                    }
                 }
             }
         }
     }
-    return ($output,$logmsg,$keysmsg,\%codehash,\%instcodes);
+    return ($output,$logmsg,$cloneinfo,$keysmsg,\%codehash,\%instcodes);
 }
 
 #############################################################
@@ -271,7 +302,7 @@
     my $xlist = 0;
     my $userkey = '';
     my $role = '';
-    my @items = ('title','optional_id','coursecode','defaultcredits','coursehome','reshome','nonstandard','adds','drops','topmap','firstres','clonecrs','clonedom','datemode','dateshift','showphotos','setpolicy','setcontent','setcomment','setkeys','keyauth','disresdis','disablechat','openall','notify_owner','notify_dc','crstype','crsquota','uniquecode');
+    my @items = ('title','optional_id','coursecode','defaultcredits','coursehome','reshome','nonstandard','adds','drops','topmap','firstres','clonecrs','clonedom','datemode','dateshift','tinyurls','showphotos','setpolicy','setcontent','setcomment','setkeys','keyauth','disresdis','disablechat','openall','notify_owner','notify_dc','crstype','crsquota','uniquecode');
     my @possroles = qw(st ad ep ta in cc co);
     my @dateitems = ('enrollstart','enrollend','accessstart','accessend','openallfrom');
     my @useritems = ('autharg','authtype','firstname','generation','lastname','middlename','studentID');
@@ -397,6 +428,7 @@
 #   ref to hash of course creation information
 #   ref to hash of role descriptions
 #   ref to scalar used to accumulate log messages
+#   ref to array used to accumulate messages about cloning
 #   ref to scalar used to accumulate messages sent to new users
 #   ref to scalar used to accumulate results of new user additions
 #   ref to hash of enrollment counts for different roles
@@ -408,6 +440,8 @@
 #       course requests submitted via course request form.
 #   optional category
 #   optional ref to scalar for six character unique identifier
+#   caller context (e.g., auto)
+#   user language handle, if caller context is 'auto'
 #
 # outputs
 #   LON-CAPA courseID for new (created) course
@@ -415,8 +449,9 @@
 #########################################################
 
 sub build_course {
-    my ($cdom,$num,$context,$details,$longroles,$logmsg,$newusermsg,$addresult,
-        $enrollcount,$output,$keysmsg,$udom,$uname,$cnum,$category,$coderef) = @_;
+    my ($cdom,$num,$context,$details,$longroles,$logmsg,$clonemsg,$newusermsg,
+        $addresult,$enrollcount,$output,$keysmsg,$udom,$uname,$cnum,$category,
+        $coderef,$callercontext,$user_lh) = @_;
     return unless (ref($details) eq 'HASH');
     my $owner_uname = $details->{'owner'};
     my $owner_domain = $details->{'domain'};
@@ -510,6 +545,9 @@
             $details->{'datemode'} = 'shift';
             $details->{'dateshift'} = 365;
         }
+        if ($details->{'tinyurls'} !~ /^(delete|transfer|create)$/) {
+            $details->{'tinyurls'} = 'create';
+        }
         my $courseargs = {
                ccuname => $details->{'owner'},
                ccdomain => $details->{'domain'},
@@ -527,6 +565,7 @@
                clonedomain => $details->{'clonedom'},
                datemode => $details->{'datemode'},
                dateshift => $details->{'dateshift'},
+               tinyurls => $details->{'tinyurls'},
                crsid => $details->{'optional_id'},
                curruser => $details->{'owner'},
                crssections => $sectionstr,
@@ -563,10 +602,14 @@
             $$logmsg .= &mt('Invalid home server for course').': '.$details->{'coursehome'};
             return;
         }
-        my ($success, $msg) = 
+        my ($success,$msg,$cloneinfo) = 
             &Apache::loncommon::construct_course($courseargs,$logmsg,\$courseid,\$crsudom,\$crsunum,
-                                                 $udom,$uname,$context,$cnum,$category,$coderef);
+                                                 $udom,$uname,$context,$cnum,$category,$coderef,
+                                                 $callercontext,$user_lh);
 	$$logmsg .= $msg;
+        if ((ref($clonemsg) eq 'ARRAY') && (ref($cloneinfo) eq 'ARRAY')) {
+             push(@{$clonemsg},@{$cloneinfo});
+        }
         if (!$success) {
             return;
         }
Index: loncom/automation/Autocreate.pl
diff -u loncom/automation/Autocreate.pl:1.21 loncom/automation/Autocreate.pl:1.22
--- loncom/automation/Autocreate.pl:1.21	Fri Jul 26 02:28:28 2019
+++ loncom/automation/Autocreate.pl	Wed Jul  1 20:09:03 2020
@@ -2,7 +2,7 @@
 #
 # Automated Course Creation script
 #
-# $Id: Autocreate.pl,v 1.21 2019/07/26 02:28:28 raeburn Exp $
+# $Id: Autocreate.pl,v 1.22 2020/07/01 20:09:03 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -175,7 +175,7 @@
     closedir(DIR);
     my %courseids = ();
     print $fh "Sending to batch - auto,$dom,$dcname,$dcdom ".join(":", at requests)."\n";
-    my ($result,$logmsg,$keysmsg,$codesref,$instcodesref) =
+    my ($result,$logmsg,$clonemsg,$keysmsg,$codesref,$instcodesref) =
         &LONCAPA::batchcreatecourse::create_courses(\@requests,\%courseids,'auto',$dom,$dcname,$dcdom);
     my $outcome;
     if ($result ne '') {
@@ -187,6 +187,9 @@
     if ($keysmsg ne '') {
         $outcome .=  $keysmsg."\n";
     }
+    if ($clonemsg ne '') {
+        $outcome .= $clonemsg."\n";
+    }
     print $fh $outcome;
 
     my $output;
@@ -267,7 +270,7 @@
     $env{'user.home'} = &Apache::lonnet::homeserver($dcname,$dcdom);
     if ($defdom ne '') {
         $env{'request.role.domain'} = $defdom;
-    } 
+    }
     return;
 }
 
Index: loncom/html/adm/help/tex/Course_Request_Clone.tex
diff -u loncom/html/adm/help/tex/Course_Request_Clone.tex:1.2 loncom/html/adm/help/tex/Course_Request_Clone.tex:1.3
--- loncom/html/adm/help/tex/Course_Request_Clone.tex:1.2	Sat Aug 20 16:06:50 2016
+++ loncom/html/adm/help/tex/Course_Request_Clone.tex	Wed Jul  1 20:09:07 2020
@@ -1,4 +1,12 @@
 \label{Course_Request_Clone}
 You may choose to build your new course by cloning an existing course. If you have Course Coordinator access or have been assigned ``cloner'' rights in an existing course currently in the LON-CAPA system you can search for the course and select it in a pop-up window, launched by the `Select Course' link.
 
-Cloning a course will copy all course content, and most course settings.  Student enrollment, peformance data and discussion postings will not be copied.  Settings which involve dates (e.g., open dates, due dates, and close dates for availability of course content and online homework problems) will be preserved, shifted by a number of days, or omitted from the copy process, based on your choice for the `Date Shift'.
+Cloning a course will copy all course content, and most course settings.  Student enrollment, performance data and discussion postings will not be copied.  Settings which involve dates (e.g., open dates, due dates, and close dates for availability of course content and online homework problems) will be preserved, shifted by a number of days, or omitted from the copy process, based on your choice for the `Date Shift'.
+
+If the existing course contains URL shortcuts (with format: /tiny/$<$domain$>$/$<$unique six character code$>$), which were originally created using Course Editor $>$ Content Utilities $>$ ``Display/Set Shortened URLs for Deep-linking'', then your options for ``URL shortcuts (for deep linking)'' include:
+\begin{enumerate}
+\item All URL shortcuts in the existing course are left alone, and none are created in the new course, or
+\item All URL shortcuts in the existing course are disassociated from their targets, but instead are now associated with the corresponding items in the new course, or
+\item All URL shortcuts in the existing course are left alone, and new shortcuts are created for the corresponding items (with their own unique six character codes).
+\end{enumerate}
+Note: If you select option 2, the existing course and new course must be in the same domain, and the same user must be the course owner of both courses, for the change to occur.  If either condition is unmet, option 3 will apply instead during the cloning process.
Index: loncom/html/adm/help/tex/Batch_Creation.tex
diff -u loncom/html/adm/help/tex/Batch_Creation.tex:1.8 loncom/html/adm/help/tex/Batch_Creation.tex:1.9
--- loncom/html/adm/help/tex/Batch_Creation.tex:1.8	Fri May 22 15:05:41 2020
+++ loncom/html/adm/help/tex/Batch_Creation.tex	Wed Jul  1 20:09:07 2020
@@ -36,6 +36,7 @@
 <clonedom>msu</clonedom>
 <datemode>shift</datemode>
 <dateshift>365</dateshift>
+<tinyurls>create</tinyurls>
 <showphotos></showphotos>
 <setpolicy>1</setpolicy>
 <setcontent>1</setcontent>
Index: doc/loncapafiles/loncapafiles.lpml
diff -u doc/loncapafiles/loncapafiles.lpml:1.1003 doc/loncapafiles/loncapafiles.lpml:1.1004
--- doc/loncapafiles/loncapafiles.lpml:1.1003	Fri May 29 18:13:20 2020
+++ doc/loncapafiles/loncapafiles.lpml	Wed Jul  1 20:09:12 2020
@@ -2,7 +2,7 @@
  "http://lpml.sourceforge.net/DTD/lpml.dtd">
 <!-- loncapafiles.lpml -->
 
-<!-- $Id: loncapafiles.lpml,v 1.1003 2020/05/29 18:13:20 raeburn Exp $ -->
+<!-- $Id: loncapafiles.lpml,v 1.1004 2020/07/01 20:09:12 raeburn Exp $ -->
 
 <!--
 
@@ -3559,6 +3559,7 @@
 Chart_Student_Data.tex;
 Chatting.tex;
 Clicker_Registration.tex;
+Clone_Tiny_URLs.tex;
 Coauthor.tex;
 Construction_Space_Overview.tex;
 Content_Page_Overview.tex;

Index: loncom/html/adm/help/tex/Clone_Tiny_URLs.tex
+++ loncom/html/adm/help/tex/Clone_Tiny_URLs.tex
\label{Clone_Tiny_URLs}
If the existing course contains URL shortcuts (with format: /tiny/$<$domain$>$/$<$unique six character code$>$), which were originally created using Course Editor $>$ Content Utilities $>$ ``Display/Set Shortened URLs for Deep-linking'', then options for ``URL shortcuts (for deep linking)'' include:
\begin{enumerate}
\item All URL shortcuts in the existing course are left alone, and none are created in the new course, or
\item All URL shortcuts in the existing course are disassociated from their targets, but instead are now associated with the corresponding items in the new course, or
\item All URL shortcuts in the existing course are left alone, and new shortcuts are created for the corresponding items (with their own unique six character codes).
\end{enumerate}
Note: If option 2 is selected, the existing course and new course must be in the same domain, and the same user must be the course owner of both courses, for the change to occur.  If either condition is unmet, option 3 will apply instead during the cloning process.


More information about the LON-CAPA-cvs mailing list