[LON-CAPA-cvs] cvs: loncom /interface loncommon.pm londocs.pm portfolio.pm /publisher lonupload.pm testbankimport.pm

raeburn raeburn@source.lon-capa.org
Sun, 28 Nov 2010 00:04:11 -0000


This is a MIME encoded message

--raeburn1290902651
Content-Type: text/plain

raeburn		Sun Nov 28 00:04:11 2010 EDT

  Modified files:              
    /loncom/interface	portfolio.pm loncommon.pm londocs.pm 
    /loncom/publisher	testbankimport.pm lonupload.pm 
  Log:
  - Work on upload of HTML file with references to embedded objects etc. 
     - Replace &process_secondary_uploads() in londocs with general purpose routines
       in loncommon.pm.
     - More comprehensive feedback on reference detection.
     - Reference updates in HTML file for changed paths originally only available 
       for upload of HTML file in course, now available for uploads in all contexts.
     - Directory traversal in original HTML file via ../ etc. accommodated.   
  
  
--raeburn1290902651
Content-Type: text/plain
Content-Disposition: attachment; filename="raeburn-20101128000411.txt"

Index: loncom/interface/portfolio.pm
diff -u loncom/interface/portfolio.pm:1.230 loncom/interface/portfolio.pm:1.231
--- loncom/interface/portfolio.pm:1.230	Thu Nov 11 23:58:05 2010
+++ loncom/interface/portfolio.pm	Sun Nov 28 00:04:05 2010
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # portfolio browser
 #
-# $Id: portfolio.pm,v 1.230 2010/11/11 23:58:05 raeburn Exp $
+# $Id: portfolio.pm,v 1.231 2010/11/28 00:04:05 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -1964,7 +1964,7 @@
 	return;
     }
 
-    my (%allfiles,%codebase,$mode);
+    my (%allfiles,%codebase,$mode,$mimetype);
     if ($env{'form.'.$formname.'.filename'} =~ m/(\.htm|\.html|\.shtml)$/i) {
         if ($env{'form.parserflag'}) {
 	    $mode = 'parse';
@@ -1974,10 +1974,12 @@
     if ($state eq 'existingfile') {
         $context = $state;
     }
+    my $subdir = $port_path.$env{'form.currentpath'};
+    $subdir =~ s{(/)$}{};
     my ($result,$timestamp) =
-	&Apache::lonnet::userfileupload($formname,$context,
-					$port_path.$env{'form.currentpath'},
-					$mode,\%allfiles,\%codebase);
+	&Apache::lonnet::userfileupload($formname,$context,$subdir,
+					$mode,\%allfiles,\%codebase,undef,undef,
+                                        undef,undef,undef,undef,\$mimetype);
     if ($state eq 'existingfile') {
         my $group_elem;
         my $rootdir = $r->dir_config('lonDaemons').'/tmp/overwrites';
@@ -2063,14 +2065,20 @@
                   ,$result,&display_file(undef,$fname)).'</span><br />');
 	$r->print(&done('Back',$url));
     } else {
-	if (%allfiles) {
-            if (!&suppress_embed_prompt()) {
-                &print_dependency_form($r,$url,\%allfiles,\%codebase);
+        if (!&suppress_embed_prompt()) {
+            if ($mimetype eq 'text/html') {
+	        if (keys(%allfiles) > 0) {
+                    &print_dependency_form($r,$url,\%allfiles,\%codebase,$result);
+                    return;
+	        } else {
+                    $r->print('<p>'.&mt('Completed upload of the file.').'<br />'.
+                              &mt('No embedded items identified.').'</p>');
+                }
             }
-	} else {
-	    $r->print(&done(undef,$url));
-	}
+        }
+	$r->print(&done(undef,$url));
     }
+    return;
 }
 
 sub hidden_elems {
@@ -2088,25 +2096,28 @@
 }
 
 sub print_dependency_form {
-    my ($r,$url,$allfiles,$codebase) = @_;
-    my $state = <<STATE;
-    <input type="hidden" name="action"      value="upload_embedded" />
-    <input type="hidden" name="currentpath" value="$env{'form.currentpath'}" />
-    <input type="hidden" name="fieldname"   value="$env{'form.fieldname'}" />
-    <input type="hidden" name="mode"        value="$env{'form.mode'}" />
-STATE
-    if ($env{'form.group'} ne '') {
-        $state .= '<input type="hidden" name="group" value="'.$env{'form.group'}.'" />'."\n";
-    }
-    my $embedded = &Apache::loncommon::ask_for_embedded_content($url,$state,$allfiles,$codebase,
+    my ($r,$url,$allfiles,$codebase,$result) = @_;
+    my $container = &HTML::Entities::encode($result,'<>"&');
+    my $state = &embedded_form_elems($container);
+    my ($embedded,$num,$pathchg) = &Apache::loncommon::ask_for_embedded_content($url,$state,$allfiles,$codebase,
                                   {'error_on_invalid_names'   => 1,
                                    'ignore_remote_references' => 1,});
     if ($embedded) {
-        $r->print('<h2>'.&mt("Reference Warning").'</h2>'.
-                  '<p>'.&mt("Completed upload of the file. This file contained references to other files. You must upload the referenced files or else the uploaded file may not work properly.").'</p>'.
-                  '<p>'.&mt("Please select the locations from which the referenced files are to be uploaded.").'</p>'.
-                  $embedded.
-                  '<p>Or '.&done('Return to directory',$url).'</p>');
+        if ($num || $pathchg) {
+            $r->print('<h3>'.&mt("Reference Warning").'</h3>');
+        } else {
+            $r->print('<h3>'.&mt("Reference Information").'</h3>');
+        }
+        if ($num) {
+            $r->print('<p>'.&mt("Completed upload of the file. This file contained references to other files. You must upload the referenced files or else the uploaded file may not work properly.").'</p>'.
+                      '<p>'.&mt("Please select the locations from which the referenced files are to be uploaded.").'</p>'.
+                       $embedded.
+                       '<p>'.&mt('or').'</p>'.&done('Return to directory',$url));
+        } else {
+            $r->print('<p>'.&mt("Completed upload of the file. This file contained references to other files.").'</p>'.
+                      $embedded.
+                      '<p>'.&done('Return to directory',$url).'</p>');
+        }
     } else {
         $r->print(&done(undef,$url));
     }
@@ -2118,7 +2129,8 @@
     my $formname = 'existingfile';
     my $port_path = &get_port_path();
     my $fname = &Apache::lonnet::clean_filename($env{'form.filename'});
-    my (%allfiles,%codebase,$mode);
+    my (%allfiles,%codebase,$mode,$mimetype);
+    my $mode;
     unless (&suppress_embed_prompt()) {
         if ($env{'form.parserflag'}) {
             if ($fname =~ /\.s?html?$/i) {
@@ -2134,24 +2146,29 @@
         return;
     }
     $env{'form.'.$formname.'.filename'} = $fname;
+    my $subdir = $port_path.$env{'form.currentpath'};
+    $subdir =~ s{(/)$}{};
     my $result=
-        &Apache::lonnet::userfileupload($formname,'overwrite',
-                                        $port_path.$env{'form.currentpath'},
-                                        $mode,\%allfiles,\%codebase);
+        &Apache::lonnet::userfileupload($formname,'overwrite',$subdir,$mode,
+                                        \%allfiles,\%codebase,undef,undef,undef,
+                                        undef,undef,undef,\$mimetype);
     if ($result !~ m|^/uploaded/|) {
         $r->print('<span class="LC_error">'.&mt('An error occurred ([_1]) while trying to overwrite [_2].'
                   ,$result,&display_file(undef,$fname)).'</span><br />');
         $r->print(&after_overwrite(&mt('Back'),$url));
     } else {
-        if (%allfiles) {
-            if (!&suppress_embed_prompt()) {
-                &print_dependency_form($r,$url,\%allfiles,\%codebase);
-            } else {
-                $r->print(&after_overwrite(undef,$url));
+        if ($mode eq 'parse') {
+            if ($mimetype eq 'text/html') {
+                if (keys(%allfiles) > 0) {
+                    &print_dependency_form($r,$url,\%allfiles,\%codebase,$result);
+                    return;
+                } else {
+                    $r->print('<p>'.&mt('Overwriting completed.').'<br />'.
+                              &mt('No embedded items identified.').'</p>');
+                }
             }
-        } else {
-            $r->print(&after_overwrite(undef,$url));
         }
+        $r->print(&after_overwrite(undef,$url));
     }
     return;
 }
@@ -2427,6 +2444,19 @@
     return $suppress_prompt;
 }
 
+sub embedded_form_elems {
+    my ($container) = @_;
+    my $state = <<STATE;
+    <input type="hidden" name="currentpath" value="$env{'form.currentpath'}" />
+    <input type="hidden" name="fieldname" value="$env{'form.fieldname'}" />
+    <input type="hidden" name="mode" value="$env{'form.mode'}" />
+    <input type="hidden" name="container" value="$container" />
+STATE
+    if ($env{'form.group'} ne '') {
+        $state .= '<input type="hidden" name="group" value="'.$env{'form.group'}.'" />'."\n";
+    }
+    return $state;
+}
 
 sub handler {
     # this handles file management
@@ -2573,10 +2603,20 @@
             my $getpropath = 1;
             my $current_disk_usage = 
                 &Apache::lonnet::diskusage($udom,$uname,$portfolio_root,$getpropath);
-	    $r->print(
+            my $container = &HTML::Entities::encode($env{'form.container'},'<>&"');
+            my $state = &embedded_form_elems($container).
+                        '<input type="hidden" name="action" value="modify_orightml" />';
+	    my ($result,$flag) =
                 &Apache::loncommon::upload_embedded('portfolio',$port_path,$uname,$udom,
-                    $group,$portfolio_root,$group,$disk_quota,$current_disk_usage));
-            $r->print(&done(undef,$url));
+                    $group,$portfolio_root,$group,$disk_quota,$current_disk_usage,$state,$url);
+            $r->print($result.&done('Return to directory',$url));
+        } else {
+            &missing_priv($r,$url,'upload');
+        }
+    } elsif ($env{'form.action'} eq 'modify_orightml') {
+        if ($can_upload) {
+            $r->print(&Apache::loncommon::modify_html_refs('portfolio',$port_path,$uname,$udom,$group,$portfolio_root,$group));
+            $r->print(&done('Return to directory',$url));
         } else {
             &missing_priv($r,$url,'upload');
         }
@@ -2651,8 +2691,9 @@
                 my $formname = 'existingfile';
                 my $fname = &Apache::lonnet::clean_filename($env{'form.filename'});
                 $env{'form.'.$formname.'.filename'} = $fname;
-                &Apache::lonnet::userfileupload($formname,'canceloverwrite',
-                                                $port_path.$env{'form.currentpath'});
+                my $subdir = $port_path.$env{'form.currentpath'};
+                $subdir =~ s{(/)$}{};
+                &Apache::lonnet::userfileupload($formname,'canceloverwrite',$subdir);
             }
         }
 	my $current_path='/';
Index: loncom/interface/loncommon.pm
diff -u loncom/interface/loncommon.pm:1.986 loncom/interface/loncommon.pm:1.987
--- loncom/interface/loncommon.pm:1.986	Sun Nov 14 18:53:51 2010
+++ loncom/interface/loncommon.pm	Sun Nov 28 00:04:05 2010
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.986 2010/11/14 18:53:51 raeburn Exp $
+# $Id: loncommon.pm,v 1.987 2010/11/28 00:04:05 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -8300,21 +8300,13 @@
 
 sub ask_for_embedded_content {
     my ($actionurl,$state,$allfiles,$codebase,$args)=@_;
-    my (%subdependencies,%dependencies,%newfiles);
+    my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges);
     my $num = 0;
-    my $upload_output;
-    foreach my $embed_file (keys(%{$allfiles})) {
-        unless ($embed_file =~ m{^\w+://} || $embed_file =~ m{^/}) {
-            my ($relpath,$fname);
-            if ($embed_file =~ m{/}) {
-                my ($path,$fname) = ($embed_file =~ m{^(.+)/([^/]*)$});
-                $subdependencies{$path}{$fname} = 1;
-            } else {
-                $dependencies{$embed_file} = 1;
-            }
-        }
-    }
-    my ($url,$udom,$uname,$getpropath);
+    my $numremref = 0;
+    my $numinvalid = 0;
+    my $numpathchg = 0;
+    my $numexisting = 0;
+    my ($output,$upload_output,$toplevel,$url,$udom,$uname,$getpropath);
     if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
         my $current_path='/';
         if ($env{'form.currentpath'}) {
@@ -8329,13 +8321,57 @@
             $uname = $env{'user.name'};
             $url = '/userfiles/portfolio';
         }
+        $toplevel = $url.'/';
         $url .= $current_path;
         $getpropath = 1;
-    } elsif ($actionurl eq '/adm/upload') {
+    } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
+             ($actionurl eq '/adm/imsimport')) { 
         ($uname,my $rest) = ($args->{'current_path'} =~ m{/priv/($match_username)/?(.*)$});
-        $url = '/home/'.$uname.'/public_html';
+        $url = '/home/'.$uname.'/public_html/';
+        $toplevel = $url;
         if ($rest ne '') {
-            $url .= '/'.$rest;
+            $url .= $rest;
+        }
+    } elsif ($actionurl eq '/adm/coursedocs') {
+        if (ref($args) eq 'HASH') {
+           $url = $args->{'docs_url'};
+           $toplevel = $url;
+        }
+    }
+    my $now = time();
+    foreach my $embed_file (keys(%{$allfiles})) {
+        my $absolutepath;
+        if ($embed_file =~ m{^\w+://}) {
+            $newfiles{$embed_file} = 1;
+            $mapping{$embed_file} = $embed_file;
+        } else {
+            if ($embed_file =~ m{^/}) {
+                $absolutepath = $embed_file;
+                $embed_file =~ s{^(/+)}{};
+            }
+            if ($embed_file =~ m{/}) {
+                my ($path,$fname) = ($embed_file =~ m{^(.+)/([^/]*)$});
+                $path = &check_for_traversal($path,$url,$toplevel);
+                my $item = $fname;
+                if ($path ne '') {
+                    $item = $path.'/'.$fname;
+                    $subdependencies{$path}{$fname} = 1;
+                } else {
+                    $dependencies{$item} = 1;
+                }
+                if ($absolutepath) {
+                    $mapping{$item} = $absolutepath;
+                } else {
+                    $mapping{$item} = $embed_file;
+                }
+            } else {
+                $dependencies{$embed_file} = 1;
+                if ($absolutepath) {
+                    $mapping{$embed_file} = $absolutepath;
+                } else {
+                    $mapping{$embed_file} = $embed_file;
+                }
+            }
         }
     }
     foreach my $path (keys(%subdependencies)) {
@@ -8346,98 +8382,225 @@
                 my ($file_name,$rest) = split(/\&/,$line,2);
                 $currsubfile{$file_name} = 1;
             }
-        } elsif ($actionurl eq '/adm/upload') {
+        } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
             if (opendir(my $dir,$url.'/'.$path)) {
                 my @subdir_list = grep(!/^\./,readdir($dir));
                 map {$currsubfile{$_} = 1;} @subdir_list;
             }
         }
         foreach my $file (keys(%{$subdependencies{$path}})) {
-            unless ($currsubfile{$file}) {
-                 $newfiles{$path.'/'.$file} = 1; 
+            if ($currsubfile{$file}) {
+                my $item = $path.'/'.$file;
+                unless ($mapping{$item} eq $item) {
+                    $pathchanges{$item} = 1;
+                }
+                $existing{$item} = 1;
+                $numexisting ++;
+            } else {
+                $newfiles{$path.'/'.$file} = 1;
             }
         }
     }
-    my (@dir_list,%currfile);
+    my %currfile;
     if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
         my @dir_list = &Apache::lonnet::dirlist($url,$udom,$uname,$getpropath);
         foreach my $line (@dir_list) {
             my ($file_name,$rest) = split(/\&/,$line,2);
             $currfile{$file_name} = 1;
         }
-    } elsif ($actionurl eq '/adm/upload') {
+    } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
         if (opendir(my $dir,$url)) {
-            @dir_list = grep(!/^\./,readdir($dir));
+            my @dir_list = grep(!/^\./,readdir($dir));
             map {$currfile{$_} = 1;} @dir_list;
         }
     }
     foreach my $file (keys(%dependencies)) {
-        unless ($currfile{$file}) {
+        if ($currfile{$file}) {
+            unless ($mapping{$file} eq $file) {
+                $pathchanges{$file} = 1;
+            }
+            $existing{$file} = 1;
+            $numexisting ++;
+        } else {
             $newfiles{$file} = 1;
         }
     }
     foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%newfiles)) {
         $upload_output .= &start_data_table_row().
-            '<td>'.$embed_file.'</td><td>';
+                          '<td><span class="LC_filename">'.$embed_file.'</span>';
+        unless ($mapping{$embed_file} eq $embed_file) {
+            $upload_output .= '<br /><span class="LC_info" style="font-size:smaller;">'.&mt('changed from: [_1]',$mapping{$embed_file}).'</span>';
+        }
+        $upload_output .= '</td><td>';
         if ($args->{'ignore_remote_references'}
             && $embed_file =~ m{^\w+://}) {
             $upload_output.='<span class="LC_warning">'.&mt("URL points to other server.").'</span>';
+            $numremref++;
         } elsif ($args->{'error_on_invalid_names'}
             && $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) {
 
-            $upload_output.='<span class="LC_warning">'.&mt("Invalid characters").'</span>';
-
+            $upload_output.='<span class="LC_warning">'.&mt('Invalid characters').'</span>';
+            $numinvalid++;
         } else {
-            $upload_output .='
-           <input name="embedded_item_'.$num.'" type="file" value="" />
-           <input name="embedded_orig_'.$num.'" type="hidden" value="'.&escape($embed_file).'" />';
-            my $attrib = join(':',@{$$allfiles{$embed_file}});
-            $upload_output .=
-                "\n\t\t".
-                '<input name="embedded_attrib_'.$num.'" type="hidden" value="'.
-                $attrib.'" />';
-            if (exists($$codebase{$embed_file})) {
-                $upload_output .=
-                    "\n\t\t".
-                    '<input name="codebase_'.$num.'" type="hidden" value="'.
-                    &escape($$codebase{$embed_file}).'" />';
-            }
+            $upload_output .= &embedded_file_element('upload_embedded',$num,
+                                                     $embed_file,\%mapping,
+                                                     $allfiles,$codebase);
+            $num++;
         }
         $upload_output .= '</td>'.&Apache::loncommon::end_data_table_row()."\n";
-        $num++;
     }
-    if ($num) {
-        $upload_output = '<form name="upload_embedded" action="'.$actionurl.'"'.
-                         ' method="post" enctype="multipart/form-data">'."\n".
-                         $state.
-                         '<b>Upload embedded files</b>:<br />'.&start_data_table().
+    foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%existing)) {
+        $upload_output .= &start_data_table_row().
+                          '<td><span class="LC_filename">'.$embed_file.'</span></td>'.
+                          '<td><span class="LC_warning">'.&mt('Already exists').'</span></td>'.
+                          &Apache::loncommon::end_data_table_row()."\n";
+    }
+    if ($upload_output) {
+        $upload_output = &start_data_table().
                          $upload_output.
-                         &Apache::loncommon::end_data_table().'<br />'."\n".
-                         '<input type ="hidden" name="number_embedded_items" value="'.$num.'" />'."\n".
-                         '<input type ="submit" value="'.&mt('Upload Listed Files').'" />'."\n".
-                         &mt('(only files for which a location has been provided will be uploaded)')."\n".
-                         '</form>';
+                         &end_data_table()."\n";
+    }
+    my $applies = 0;
+    if ($numremref) {
+        $applies ++;
+    }
+    if ($numinvalid) {
+        $applies ++;
+    }
+    if ($numexisting) {
+        $applies ++;
+    }
+    if ($num) {
+        $output = '<form name="upload_embedded" action="'.$actionurl.'"'.
+                  ' method="post" enctype="multipart/form-data">'."\n".
+                  $state.
+                  '<h3>'.&mt('Upload embedded files').
+                  ':</h3>'.$upload_output.'<br />'."\n".
+                  '<input type ="hidden" name="number_embedded_items" value="'.
+                  $num.'" />'."\n";
+        if ($actionurl eq '') {
+            $output .=  '<input type="hidden" name="phase" value="three" />';
+        }
+    } elsif ($applies) {
+        $output = '<b>'.&mt('Referenced files').'</b>:<br />';
+        if ($applies > 1) {
+            $output .=  
+                &mt('No files need to be uploaded, as one of the following applies to each reference:').'<ul>';
+            if ($numremref) {
+                $output .= '<li>'.&mt('reference is to a URL which points to another server').'</li>'."\n";
+            }
+            if ($numinvalid) {
+                $output .= '<li>'.&mt('reference is to file with a name containing invalid characters').'</li>'."\n";
+            }
+            if ($numexisting) {
+                $output .= '<li>'.&mt('reference is to an existing file at the specified location').'</li>'."\n";
+            }
+            $output .= '</ul><br />';
+        } elsif ($numremref) {
+            $output .= '<p>'.&mt('None to upload, as all references are to URLs pointing to another server.').'</p>';
+        } elsif ($numinvalid) {
+            $output .= '<p>'.&mt('None to upload, as all references are to files with names containing invalid characters.').'</p>';
+        } elsif ($numexisting) {
+            $output .= '<p>'.&mt('None to upload, as all references are to existing files.').'</p>';
+        }
+        $output .= $upload_output.'<br />';
+    }
+    my ($pathchange_output,$chgcount);
+    $chgcount = $num;
+    if (keys(%pathchanges) > 0) {
+        foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%pathchanges)) {
+            if ($num) {
+                $output .= &embedded_file_element('pathchange',$chgcount,
+                                                  $embed_file,\%mapping,
+                                                  $allfiles,$codebase);
+            } else {
+                $pathchange_output .= 
+                    &start_data_table_row().
+                    '<td><input type ="checkbox" name="namechange" value="'.
+                    $chgcount.'" checked="checked" /></td>'.
+                    '<td>'.$mapping{$embed_file}.'</td>'.
+                    '<td>'.$embed_file.
+                    &embedded_file_element('pathchange',$numpathchg,$embed_file,
+                                           \%mapping,$allfiles,$codebase).
+                    '</td>'.&end_data_table_row();
+            }
+            $numpathchg ++;
+            $chgcount ++;
+        }
+    }
+    if ($num) {
+        if ($numpathchg) {
+            $output .= '<input type ="hidden" name="number_pathchange_items" value="'.
+                       $numpathchg.'" />'."\n";
+        }
+        if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') || 
+            ($actionurl eq '/adm/imsimport')) {
+            $output .= '<input type="hidden" name="phase" value="three" />'."\n";
+        } elsif ($actionurl eq '/adm/portfolio' || $actionurl eq '/adm/coursegrp_portfolio') {
+            $output .= '<input type="hidden" name="action" value="upload_embedded" />';
+        }
+        $output .=  '<input type ="submit" value="'.&mt('Upload Listed Files').'" />'."\n".
+                    &mt('(only files for which a location has been provided will be uploaded)').'</form>'."\n";
+    } elsif ($numpathchg) {
+        my %pathchange = ();
+        $output .= &modify_html_form('pathchange',$actionurl,$state,\%pathchange,$pathchange_output);
+        if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
+            $output .= '<p>'.&mt('or').'</p>'; 
+        } 
     }
-    return $upload_output;
+    return ($output,$num,$numpathchg);
+}
+
+sub embedded_file_element {
+    my ($context,$num,$embed_file,$mapping,$allfiles,$codebase) = @_;
+    return unless ((ref($mapping) eq 'HASH') && (ref($allfiles) eq 'HASH') &&
+                   (ref($codebase) eq 'HASH'));
+    my $output;
+    if ($context eq 'upload_embedded') {
+       $output = '<input name="embedded_item_'.$num.'" type="file" value="" />'."\n";
+    }
+    $output .= '<input name="embedded_orig_'.$num.'" type="hidden" value="'.
+               &escape($embed_file).'" />';
+    unless (($context eq 'upload_embedded') && 
+            ($mapping->{$embed_file} eq $embed_file)) {
+        $output .='
+        <input name="embedded_ref_'.$num.'" type="hidden" value="'.&escape($mapping->{$embed_file}).'" />';
+    }
+    my $attrib;
+    if (ref($allfiles->{$mapping->{$embed_file}}) eq 'ARRAY') {
+        $attrib = &escape(join(':',@{$allfiles->{$mapping->{$embed_file}}}));
+    }
+    $output .=
+        "\n\t\t".
+        '<input name="embedded_attrib_'.$num.'" type="hidden" value="'.
+        $attrib.'" />';
+    if (exists($codebase->{$mapping->{$embed_file}})) {
+        $output .=
+            "\n\t\t".
+            '<input name="codebase_'.$num.'" type="hidden" value="'.
+            &escape($codebase->{$mapping->{$embed_file}}).'" />';
+    }
+    return $output;
 }
 
 sub upload_embedded {
     my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota,
-        $current_disk_usage) = @_;
-    my $output;
+        $current_disk_usage,$hiddenstate,$actionurl) = @_;
+    my (%pathchange,$output,$modifyform,$footer,$returnflag);
     for (my $i=0; $i<$env{'form.number_embedded_items'}; $i++) {
         next if (!exists($env{'form.embedded_item_'.$i.'.filename'}));
         my $orig_uploaded_filename =
             $env{'form.embedded_item_'.$i.'.filename'};
-
-        $env{'form.embedded_orig_'.$i} =
-            &unescape($env{'form.embedded_orig_'.$i});
+        foreach my $type ('orig','ref','attrib','codebase') {
+            if ($env{'form.embedded_'.$type.'_'.$i} ne '') {
+                $env{'form.embedded_'.$type.'_'.$i} =
+                    &unescape($env{'form.embedded_'.$type.'_'.$i});
+            }
+        }
         my ($path,$fname) =
             ($env{'form.embedded_orig_'.$i} =~ m{(.*/)([^/]*)});
         # no path, whole string is fname
         if (!$fname) { $fname = $env{'form.embedded_orig_'.$i} };
-
-        $path = $env{'form.currentpath'}.$path;
         $fname = &Apache::lonnet::clean_filename($fname);
         # See if there is anything left
         next if ($fname eq '');
@@ -8449,7 +8612,8 @@
             if ($group ne '') {
                 $port_path = "groups/$group/$port_path";
             }
-            ($state,$msg) = &check_for_upload($path,$fname,$group,'embedded_item_'.$i,
+            ($state,$msg) = &check_for_upload($env{'form.currentpath'}.$path,
+                                              $fname,$group,'embedded_item_'.$i,
                                               $dir_root,$port_path,$disk_quota,
                                               $current_disk_usage,$uname,$udom);
             if ($state eq 'will_exceed_quota'
@@ -8467,14 +8631,14 @@
         # Check if extension is valid
         if (($fname =~ /\.(\w+)$/) &&
             (&Apache::loncommon::fileembstyle($1) eq 'hdn')) {
-            $output .= &mt('Invalid file extension ([_1]) - reserved for LONCAPA use - rename the file with a different extension and re-upload. ',$1);
+            $output .= &mt('Invalid file extension ([_1]) - reserved for LONCAPA use - rename the file with a different extension and re-upload. ',$1).'<br />';
             next;
         } elsif (($fname =~ /\.(\w+)$/) &&
                  (!defined(&Apache::loncommon::fileembstyle($1)))) {
-            $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1);
+            $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1).'<br />';
             next;
         } elsif ($fname=~/\.(\d+)\.(\w+)$/) {
-            $output .= &mt('File name not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2);
+            $output .= &mt('File name not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2).'<br />';
             next;
         }
 
@@ -8484,11 +8648,12 @@
             if ($state eq 'existingfile') {
                 $result=
                     &Apache::lonnet::userfileupload('embedded_item_'.$i,'existingfile',
-                                                    $dirpath.$path,);
+                                                    $dirpath.$env{'form.currentpath'}.$path);
             } else {
                 $result=
                     &Apache::lonnet::userfileupload('embedded_item_'.$i,'',
-                                                    $dirpath.$path);
+                                                    $dirpath.
+                                                    $env{'form.currentpath'}.$path);
                 if ($result !~ m|^/uploaded/|) {
                     $output .= '<span class="LC_error">'
                                .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
@@ -8496,10 +8661,24 @@
                                .'</span><br />';
                     next;
                 } else {
-                    $output .= '<p>'.&mt('Uploaded [_1]','<span class="LC_filename">'.
-                               $path.$fname.'</span>').'</p>';     
+                    $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
+                               $path.$fname.'</span>').'<br />';     
                 }
             }
+        } elsif ($context eq 'coursedoc') {
+            my $result =
+                &Apache::lonnet::userfileupload('embedded_item_'.$i,'coursedoc',
+                                                $dirpath.'/'.$path);
+            if ($result !~ m|^/uploaded/|) {
+                $output .= '<span class="LC_error">'
+                           .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
+                           ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
+                           .'</span><br />';
+                    next;
+            } else {
+                $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
+                           $path.$fname.'</span>').'<br />';
+            }
         } else {
 # Save the file
             my $target = $env{'form.embedded_item_'.$i};
@@ -8528,19 +8707,190 @@
                               &mt('An error occurred while writing the file [_1] for embedded element [_2].',$orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
                               '</span><br />';
                 } else {
-                    if ($context eq 'testbank') {
-                        $output .= &mt('Embedded file uploaded successfully:').
-                                   '&nbsp;<a href="'.$url.'">'.
-                                   $orig_uploaded_filename.'</a><br />';
-                    } else {
-                        $output .= '<span class=\"LC_fontsize_large\">'.
-                                   &mt('View embedded file: [_1]','<a href="'.$url.'">'.
-                                   $orig_uploaded_filename.'</a>').'</span><br />';
+                    $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
+                               $url.'</span>').'<br />';
+                    unless ($context eq 'testbank') {
+                        $footer .= &mt('View embedded file: [_1]',
+                                       '<a href="'.$url.'">'.$fname.'</a>').'<br />';
                     }
                 }
                 close($fh);
             }
         }
+        if ($env{'form.embedded_ref_'.$i}) {
+            $pathchange{$i} = 1;
+        }
+    }
+    if ($output) {
+        $output = '<p>'.$output.'</p>';
+    }
+    $output .= &modify_html_form('upload_embedded',$actionurl,$hiddenstate,\%pathchange);
+    $returnflag = 'ok';
+    if (keys(%pathchange) > 0) {
+        if ($context eq 'portfolio') {
+            $output .= '<p>'.&mt('or').'</p>';
+        } elsif ($context eq 'testbank') {
+            $output .=  '<p>'.&mt('Or [_1]continue[_2] the testbank import without modifying the references(s).','<a href="javascript:document.testbankForm.submit();">','</a>').'</p>';
+            $returnflag = 'modify_orightml';
+        }
+    }
+    return ($output.$footer,$returnflag);
+}
+
+sub modify_html_form {
+    my ($context,$actionurl,$hiddenstate,$pathchange,$pathchgtable) = @_;
+    my $end = 0;
+    my $modifyform;
+    if ($context eq 'upload_embedded') {
+        return unless (ref($pathchange) eq 'HASH');
+        if ($env{'form.number_embedded_items'}) {
+            $end += $env{'form.number_embedded_items'};
+        }
+        if ($env{'form.number_pathchange_items'}) {
+            $end += $env{'form.number_pathchange_items'};
+        }
+        if ($end) {
+            for (my $i=0; $i<$end; $i++) {
+                if ($i < $env{'form.number_embedded_items'}) {
+                    next unless($pathchange->{$i});
+                }
+                $modifyform .=
+                    &start_data_table_row().
+                    '<td><input type ="checkbox" name="namechange" value="'.$i.'" '.
+                    'checked="checked" /></td>'.
+                    '<td>'.$env{'form.embedded_ref_'.$i}.
+                    '<input type="hidden" name="embedded_ref_'.$i.'" value="'.
+                    &escape($env{'form.embedded_ref_'.$i}).'" />'.
+                    '<input type="hidden" name="embedded_codebase_'.$i.'" value="'.
+                    &escape($env{'form.embedded_codebase_'.$i}).'" />'.
+                    '<input type="hidden" name="embedded_attrib_'.$i.'" value="'.
+                    &escape($env{'form.embedded_attrib_'.$i}).'" /></td>'.
+                    '<td>'.$env{'form.embedded_orig_'.$i}.
+                    '<input type="hidden" name="embedded_orig_'.$i.'" value="'.
+                    &escape($env{'form.embedded_orig_'.$i}).'" /></td>'.
+                    &end_data_table_row();
+            } 
+        }
+    } else {
+        $modifyform = $pathchgtable;
+        if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
+            $hiddenstate .= '<input type="hidden" name="phase" value="four" />';
+        } elsif (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
+            $hiddenstate .= '<input type="hidden" name="action" value="modify_orightml" />';
+        }
+    }
+    if ($modifyform) {
+        return '<h3>'.&mt('Changes in content of HTML file required').'</h3>'."\n".
+               '<p>'.&mt('Changes need to be made to the reference(s) used for one or more of the dependencies, if your HTML file is to work correctly:').'<ol>'."\n".
+               '<li>'.&mt('For consistency between the reference(s) and the location of the corresponding stored file within LON-CAPA.').'</li>'."\n".
+               '<li>'.&mt('To change absolute paths to relative paths, or replace directory traversal via "../" within the original reference.').'</li>'."\n".
+               '</ol></p>'."\n".'<p>'.
+               &mt('LON-CAPA can make the required changes to your HTML file.').'</p>'."\n".
+               '<form method="post" name="refchanger" action="'.$actionurl.'">'.
+               &start_data_table()."\n".
+               &start_data_table_header_row().
+               '<th>'.&mt('Change?').'</th>'.
+               '<th>'.&mt('Current reference').'</th>'.
+               '<th>'.&mt('Required reference').'</th>'.
+               &end_data_table_header_row()."\n".
+               $modifyform.
+               &end_data_table().'<br />'."\n".$hiddenstate.
+               '<input type="submit" name="pathchanges" value="'.&mt('Modify HTML file').'" />'.
+               '</form>'."\n";
+    }
+    return;
+}
+
+sub modify_html_refs {
+    my ($context,$dirpath,$uname,$udom,$dir_root) = @_;
+    my $container;
+    if ($context eq 'portfolio') {
+        $container = $env{'form.container'};
+    } elsif ($context eq 'coursedoc') {
+        $container = $env{'form.primaryurl'};
+    } else {
+        $container = $env{'form.filename'};
+        $container =~ s{^/priv/(\Q$uname\E)/(.*)}{/home/$1/public_html/$2};
+    }
+    my (%allfiles,%codebase,$output,$content);
+    my @changes = &get_env_multiple('form.namechange');
+    return unless (@changes > 0);
+    if (($context eq 'portfolio') || ($context eq 'coursedoc')) {
+        return unless ($container =~ m{^/uploaded/\Q$udom\E/\Q$uname\E/});
+        $content = &Apache::lonnet::getfile($container);
+        return if ($content eq '-1');
+    } else {
+        return unless ($container =~ /^\Q$dir_root\E/); 
+        if (open(my $fh,"<$container")) {
+            $content = join('', <$fh>);
+            close($fh);
+        } else {
+            return;
+        }
+    }
+    my ($count,$codebasecount) = (0,0);
+    my $mm = new File::MMagic;
+    my $mime_type = $mm->checktype_contents($content);
+    if ($mime_type eq 'text/html') {
+        my $parse_result = 
+            &Apache::lonnet::extract_embedded_items($container,\%allfiles,
+                                                    \%codebase,\$content);
+        if ($parse_result eq 'ok') {
+            foreach my $i (@changes) {
+                my $orig = &unescape($env{'form.embedded_orig_'.$i});
+                my $ref = &unescape($env{'form.embedded_ref_'.$i});
+                if ($allfiles{$ref}) {
+                    my $newname =  $orig;
+                    my ($attrib_regexp,$codebase);
+                    my $attrib_regexp = &unescape($env{'form.embedded_attrib_'.$i});
+                    if ($attrib_regexp =~ /:/) {
+                        $attrib_regexp =~ s/\:/|/g;
+                    }
+                    if ($content =~ m{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
+                        my $numchg = ($content =~ s{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
+                        $count += $numchg;
+                    }
+                    if ($env{'form.embedded_codebase_'.$i} ne '') {
+                        my $codebase = &unescape($env{'form.embedded_codebase_'.$i});
+                        my $numchg = ($content =~ s/(codebase\s*=\s*["']?)\Q$codebase\E(["']?)/$1.$2/i); #' stupid emacs
+                        $codebasecount ++;
+                    }
+                }
+            }
+            if ($count || $codebasecount) {
+                my $saveresult;
+                if ($context eq 'portfolio' || $context eq 'coursedoc') {
+                    my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
+                    if ($url eq $container) {
+                        my ($fname) = ($container =~ m{/([^/]+)$});
+                        $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
+                                            $count,'<span class="LC_filename">'.
+                                            $fname.'</span>').'</p>'; 
+                    } else {
+                         $output = '<p class="LC_error">'.
+                                   &mt('Error: update failed for: [_1].',
+                                   '<span class="LC_filename">'.
+                                   $container.'</span>').'</p>';
+                    }
+                } else {
+                    if (open(my $fh,">$container")) {
+                        print $fh $content;
+                        close($fh);
+                        $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
+                                  $count,'<span class="LC_filename">'.
+                                  $container.'</span>').'</p>';
+                    } else {
+                         $output = '<p class="LC_error">'.
+                                   &mt('Error: could not update [_1].',
+                                   '<span class="LC_filename">'.
+                                   $container.'</span>').'</p>';
+                    }
+                }
+            }
+        } else {
+            &logthis('Failed to parse '.$container.
+                     ' to modify references: '.$parse_result);
+        }
     }
     return $output;
 }
@@ -8628,6 +8978,48 @@
     }
 }
 
+sub check_for_traversal {
+    my ($path,$url,$toplevel) = @_;
+    my @parts=split(/\//,$path);
+    my $cleanpath;
+    my $fullpath = $url;
+    for (my $i=0;$i<@parts;$i++) {
+        next if ($parts[$i] eq '.');
+        if ($parts[$i] eq '..') {
+            $fullpath =~ s{([^/]+/)$}{};
+        } else {
+            $fullpath .= $parts[$i].'/';
+        }
+    }
+    if ($fullpath =~ /^\Q$url\E(.*)$/) {
+        $cleanpath = $1;
+    } elsif ($fullpath =~ /^\Q$toplevel\E(.*)$/) {
+        my $curr_toprel = $1;
+        my @parts = split(/\//,$curr_toprel);
+        my ($url_toprel) = ($url =~ /^\Q$toplevel\E(.*)$/);
+        my @urlparts = split(/\//,$url_toprel);
+        my $doubledots;
+        my $startdiff = -1;
+        for (my $i=0; $i<@urlparts; $i++) {
+            if ($startdiff == -1) {
+                unless ($urlparts[$i] eq $parts[$i]) {
+                    $startdiff = $i;
+                    $doubledots .= '../';
+                }
+            } else {
+                $doubledots .= '../';
+            }
+        }
+        if ($startdiff > -1) {
+            $cleanpath = $doubledots;
+            for (my $i=$startdiff; $i<@parts; $i++) {
+                $cleanpath .= $parts[$i].'/';
+            }
+        }
+    }
+    $cleanpath =~ s{(/)$}{};
+    return $cleanpath;
+}
 
 =pod
 
Index: loncom/interface/londocs.pm
diff -u loncom/interface/londocs.pm:1.439 loncom/interface/londocs.pm:1.440
--- loncom/interface/londocs.pm:1.439	Wed Oct 27 01:00:38 2010
+++ loncom/interface/londocs.pm	Sun Nov 28 00:04:05 2010
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Documents
 #
-# $Id: londocs.pm,v 1.439 2010/10/27 01:00:38 raeburn Exp $
+# $Id: londocs.pm,v 1.440 2010/11/28 00:04:05 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -1682,11 +1682,10 @@
 sub process_file_upload {
     my ($upload_output,$coursenum,$coursedom,$allfiles,$codebase,$uploadcmd) = @_;
 # upload a file, if present
-    my $parseaction;
-   if ($env{'form.parserflag'}) {
+    my ($parseaction,$showupload,$nextphase,$mimetype);
+    if ($env{'form.parserflag'}) {
         $parseaction = 'parse';
     }
-    my $phase_status;
     my $folder=$env{'form.folder'};
     if ($folder eq '') {
         $folder='default';
@@ -1705,7 +1704,8 @@
             $LONCAPA::map::resources[1]='';
         }
         if ($fatal) {
-            return 'failed';
+            $$upload_output = '<p><span class="LC_error">'.&mt('The uploaded file has not been stored as an error occurred reading the contents of the current folder.').'</span></p>';
+            return;
         }
         my $destination = 'docs/';
         if ($folder =~ /^supplemental/) {
@@ -1716,13 +1716,23 @@
         } elsif ($folder =~ /^(default|supplemental)_(\d+)$/) {
             $destination .=  $2.'/';
         }
-# this is for a course, not a user, so set coursedoc flag
-# probably the only place in the system where this should be "1"
+# this is for a course, not a user, so set context to coursedoc.
         my $newidx=&LONCAPA::map::getresidx();
         $destination .= $newidx;
         my $url=&Apache::lonnet::userfileupload('uploaddoc','coursedoc',$destination,
 						$parseaction,$allfiles,
-						$codebase);
+						$codebase,undef,undef,undef,undef,
+                                                undef,undef,\$mimetype);
+        if ($url =~ m{^/uploaded/\Q$coursedom\E/\Q$coursenum\E.*/([^/]+)$}) {
+            my $stored = $1;
+            $showupload = '<p>'.&mt('Uploaded [_1]','<span class="LC_filename">'.
+                          $stored.'</span>').'</p>';
+        } else {
+            my ($filename) = ($env{'form.uploaddoc.filename'} =~ m{([^/]+)$});
+            
+            $$upload_output = '<p><span class="LC_error">'.&mt('Unable to save file [_1].','<span class="LC_filename">'.$filename.'</span>').'</span></p>';
+            return;
+        }
         my $ext='false';
         if ($url=~m{^http://}) { $ext='true'; }
 	$url     = &LONCAPA::map::qtunescape($url);
@@ -1739,51 +1749,37 @@
         ($errtext,$fatal)=&storemap($coursenum,$coursedom,
 				    $folder.'.'.$container);
         if ($fatal) {
-            $$upload_output .= '<p><span class="LC_error">'.$errtext.'</span></p>';
-            return 'failed';
+            $$upload_output = '<p><span class="LC_error">'.$errtext.'</span></p>';
+            return;
         } else {
-            if ($parseaction eq 'parse') {
+            if ($parseaction eq 'parse' && $mimetype eq 'text/html') {
+                $$upload_output = $showupload;
                 my $total_embedded = scalar(keys(%{$allfiles}));
                 if ($total_embedded > 0) {
-                    my $num = 0;
-		    my $state = '
-   <input type="hidden" name="folderpath" value="'.&HTML::Entities::encode($env{'form.folderpath'},'<>&"').'" />
-   <input type="hidden" name="cmd" value="upload_embedded" />
-   <input type="hidden" name="newidx" value="'.$newidx.'" />
-   <input type="hidden" name="primaryurl" value="'.&escape($url).'" />
-   <input type="hidden" name="phasetwo" value="'.$total_embedded.'" />';
-		    $phase_status = 'phasetwo';
-
-                    $$upload_output .=
-			'This file contains embedded multimedia objects, which need to be uploaded to LON-CAPA.<br />'.
-			&Apache::loncommon::ask_for_embedded_content(
-                            '/adm/coursedocs',$state,$allfiles,$codebase);
+                    my $uploadphase = 'upload_embedded';
+                    my $primaryurl = &HTML::Entities::encode($url,'<>&"');
+		    my $state = &embedded_form_elems($uploadphase,$primaryurl,$newidx); 
+                    my ($embedded,$num) = 
+                        &Apache::loncommon::ask_for_embedded_content(
+                            '/adm/coursedocs',$state,$allfiles,$codebase,{'docs_url' => $url});
+                    if ($embedded) {
+                        if ($num) {
+                            $$upload_output .=
+			         '<p>'.&mt('This file contains embedded multimedia objects, which need to be uploaded.').'</p>'.$embedded;
+                            $nextphase = $uploadphase;
+                        } else {
+                            $$upload_output .= $embedded;
+                        }
+                    } else {
+                        $$upload_output .= &mt('Embedded item(s) already present, so no additional upload(s) required').'<br />';
+                    }
                 } else {
-                    $$upload_output .= 'No embedded items identified<br />';
+                    $$upload_output .= &mt('No embedded items identified').'<br />';
                 }
             }
         }
     }
-    return $phase_status;
-}
-
-sub process_secondary_uploads {
-    my ($upload_output,$coursedom,$coursenum,$formname,$num,$newidx) = @_;
-    my $folder=$env{'form.folder'};
-    my $destination = 'docs/';
-    if ($folder =~ /^supplemental/) {
-        $destination = 'supplemental/';
-    }
-    if (($folder eq 'default') || ($folder eq 'supplemental')) {
-        $destination .= 'default/';
-    } elsif ($folder =~ /^(default|supplemental)_(\d+)$/) {
-        $destination .=  $2.'/';
-    }
-    $destination .= $newidx;
-    my ($url,$filename);
-    $url=&Apache::lonnet::userfileupload($formname.$num,'coursedoc',$destination);
-    ($filename) = ($url =~ m{^/uploaded/\Q$coursedom\E/\Q$coursenum\E/\Q$destination\E/(.+)$});
-    return $filename;
+    return $nextphase;
 }
 
 sub is_supplemental_title {
@@ -1848,7 +1844,7 @@
     }
     if ($env{'form.pagepath'}) {
         $type = $container = 'page';
-        $esc_path=&escape($path = $env{'form.pagepath'});
+        $esc_path=&escape($env{'form.pagepath'});
 	$path = &HTML::Entities::encode($env{'form.pagepath'},'<>&"');
         $symb=&escape($env{'form.pagesymb'});
     }
@@ -2825,92 +2821,49 @@
 
   my %allfiles = ();
   my %codebase = ();
-  my ($upload_result,$upload_output);
+  my ($upload_result,$upload_output,$uploadphase);
   if ($allowed) {
       if (($env{'form.uploaddoc.filename'}) &&
 	  ($env{'form.cmd'}=~/^upload_(\w+)/)) {
-# Process file upload - phase one - upload and parse primary file.
+          my $context = $1; 
+          # Process file upload - phase one - upload and parse primary file.
 	  undef($hadchanges);
-          $upload_result = &process_file_upload(\$upload_output,$coursenum,
-						$coursedom,\%allfiles,
-						\%codebase,$1);
+          $uploadphase = &process_file_upload(\$upload_output,$coursenum,$coursedom,
+                                              \%allfiles,\%codebase,$context);
 	  if ($hadchanges) {
 	      &mark_hash_old();
 	  }
-          if ($upload_result eq 'phasetwo') {
-              $r->print($upload_output);
-          }
-      } elsif ($env{'form.phasetwo'}) {
-          my %newname = ();
-          my %origname = ();
-          my %attribs = ();
-          my $updateflag = 0;
-          my $residx = $env{'form.newidx'};
-          my $primary_url = &unescape($env{'form.primaryurl'});
-# Process file upload - phase two - gather secondary files.
-          for (my $i=0; $i<$env{'form.phasetwo'}; $i++) {
-              if ($env{'form.embedded_item_'.$i.'.filename'}) {
-                  my $javacodebase;
-                  $newname{$i} = &process_secondary_uploads(\$upload_output,$coursedom,$coursenum,'embedded_item_',$i,$residx);
-                  $origname{$i} = &unescape($env{'form.embedded_orig_'.$i});
-                  if (exists($env{'form.embedded_codebase_'.$i})) {
-                      $javacodebase =  &unescape($env{'form.embedded_codebase_'.$i});
-                      $origname{$i} =~ s#^\Q$javacodebase\E/##;
-                  }
-                  my @attributes = ();
-                  if ($env{'form.embedded_attrib_'.$i} =~ /:/) {
-                      @attributes = split(/:/,$env{'form.embedded_attrib_'.$i});
-                  } else {
-                      @attributes = ($env{'form.embedded_attrib_'.$i});
-                  }
-                  foreach my $attr (@attributes) {
-                      push(@{$attribs{$i}},&unescape($attr));
-                  }
-                  if ($javacodebase) {
-                      $codebase{$i} = $javacodebase;
-                      $codebase{$i} =~ s#/$##;
-                      $updateflag = 1;
-                  }
-              }
-              unless ($newname{$i} eq $origname{$i}) {
-                  $updateflag = 1;
-              }
-          }
-# Process file upload - phase three - modify primary file
-          if ($updateflag) {
-              my ($content,$rtncode);
-              my $updateflag = 0;
-              my $getstatus = &Apache::lonnet::getuploaded('GET',$primary_url,$coursedom,$coursenum,\$content,\$rtncode);
-              if ($getstatus eq 'ok') {
-                  foreach my $item (keys(%newname)) {
-                      if ($newname{$item} ne $origname{$item}) {
-                          my $attrib_regexp = '';
-                          if (@{$attribs{$item}} > 1) {
-                              $attrib_regexp = join('|',@{$attribs{$item}});
-                          } else {
-                              $attrib_regexp = $attribs{$item}[0];
-                          }
-                          if ($content =~ m#($attrib_regexp\s*=\s*['"]?)\Q$origname{$item}\E(['"]?)#) {
-                          }
-                          $content =~ s#($attrib_regexp\s*=\s*['"]?)\Q$origname{$item}\E(['"]?)#$1$newname{$item}$2#gi;
-                      }
-                      if (exists($codebase{$item})) {
-                          $content =~ s/(codebase\s*=\s*["']?)\Q$codebase{$item}\E(["']?)/$1.$2/i; #' stupid emacs
-                      }
-                  }
-# Save edited file.
-                  my $saveresult;
-                  my $docuname=$env{'course.'.$env{'request.course.id'}.'.num'};
-                  my $docudom=$env{'course.'.$env{'request.course.id'}.'.domain'};
-                  my $url = &Apache::lonnet::store_edited_file($primary_url,$content,$docudom,$docuname,\$saveresult);
-              } else {
-                  &Apache::lonnet::logthis('retrieval of uploaded file - '.$primary_url.' - for editing, failed: '.$getstatus);
-              }
-          }
+          $r->print($upload_output);
+      } elsif ($env{'form.phase'} eq 'upload_embedded') {
+          # Process file upload - phase two - upload embedded objects 
+          $uploadphase = 'check_embedded';
+          my $primaryurl = &HTML::Entities::encode($env{'form.primaryurl'},'<>&"');   
+          my $state = &embedded_form_elems($uploadphase,$primaryurl,
+                                           $env{'form.newidx'});
+          my $docuname=$env{'course.'.$env{'request.course.id'}.'.num'};
+          my $docudom=$env{'course.'.$env{'request.course.id'}.'.domain'};
+          my ($destination,$dir_root) = &embedded_destination();
+          my $url_root = '/uploaded/'.$docudom.'/'.$docuname;
+          my $actionurl = '/adm/coursedocs';
+          my ($result,$flag) = 
+              &Apache::loncommon::upload_embedded('coursedoc',$destination,
+                  $docuname,$docudom,$dir_root,$url_root,undef,undef,undef,$state,
+                  $actionurl);
+          $r->print($result.&return_to_editor());
+      } elsif ($env{'form.phase'} eq 'check_embedded') {
+          # Process file upload - phase three - modify references in HTML file
+          $uploadphase = 'modified_orightml';
+          my $docuname=$env{'course.'.$env{'request.course.id'}.'.num'};
+          my $docudom=$env{'course.'.$env{'request.course.id'}.'.domain'};
+          my ($destination,$dir_root) = &embedded_destination();
+          $r->print(&Apache::loncommon::modify_html_refs('coursedoc',$destination,
+                                                         $docuname,$docudom,undef,
+                                                         $dir_root).
+                   &return_to_editor());
       }
   }
 
-  unless ($showdoc ||  $upload_result eq 'phasetwo') {
+  unless ($showdoc || $uploadphase) {  
 # -----------------------------------------------------------------------------
        my %lt=&Apache::lonlocal::texthash(
                 'uplm' => 'Upload a new main '.lc($crstype).' document',
@@ -3468,7 +3421,7 @@
 </form>');
     }
   } else {
-      unless ($upload_result eq 'phasetwo') {
+      unless ($uploadphase) {
 # -------------------------------------------------------- This is showdoc mode
           $r->print("<h1>".&mt('Uploaded Document').' - '.
 		&Apache::lonnet::gettitle($r->uri).'</h1><p>'.
@@ -3481,6 +3434,42 @@
  return OK;
 }
 
+sub embedded_form_elems {
+    my ($phase,$primaryurl,$newidx) = @_;
+    my $folderpath = &HTML::Entities::encode($env{'form.folderpath'},'<>&"');
+    return <<STATE;
+    <input type="hidden" name="folderpath" value="$folderpath" />
+    <input type="hidden" name="cmd" value="upload_embedded" />
+    <input type="hidden" name="newidx" value="$newidx" />
+    <input type="hidden" name="phase" value="$phase" />
+    <input type="hidden" name="primaryurl" value="$primaryurl" />
+STATE
+}
+
+sub embedded_destination {
+    my $folder=$env{'form.folder'};
+    my $destination = 'docs/';
+    if ($folder =~ /^supplemental/) {
+        $destination = 'supplemental/';
+    }
+    if (($folder eq 'default') || ($folder eq 'supplemental')) {
+        $destination .= 'default/';
+    } elsif ($folder =~ /^(default|supplemental)_(\d+)$/) {
+        $destination .=  $2.'/';
+    }
+    $destination .= $env{'form.newidx'};
+    my $dir_root = '/userfiles';
+    return ($destination,$dir_root);
+}
+
+sub return_to_editor {
+    my $actionurl = '/adm/coursedocs';
+    return '<p><form name="backtoeditor" method="post" action="'.$actionurl.'" />'."\n". 
+           '<input type="hidden" name="folderpath" value="'.&HTML::Entities::encode($env{'form.folderpath'},'<>&"').'" /></form>'."\n".
+           '<a href="javascript:document.backtoeditor.submit();">'.&mt('Return to Editor').
+           '</a></p>';
+}
+
 sub generate_admin_options {
   my ($help_ref,$env_ref) = @_;
   my %lt=&Apache::lonlocal::texthash(
Index: loncom/publisher/testbankimport.pm
diff -u loncom/publisher/testbankimport.pm:1.26 loncom/publisher/testbankimport.pm:1.27
--- loncom/publisher/testbankimport.pm:1.26	Sat Nov 27 22:43:32 2010
+++ loncom/publisher/testbankimport.pm	Sun Nov 28 00:04:10 2010
@@ -1,5 +1,5 @@
 # Handler for parsing text upload problem descriptions into .problems
-# $Id: testbankimport.pm,v 1.26 2010/11/27 22:43:32 raeburn Exp $
+# $Id: testbankimport.pm,v 1.27 2010/11/28 00:04:10 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -1906,15 +1906,32 @@
     }
 
     $r->print(&print_header($uname,$udom,$javascript,\%loadentries,$title,
-              $current_page,\@pages,\%names));
+                            $current_page,\@pages,\%names));
 
-    if ($env{'form.phase'} eq 'three') {
-        if ($env{'form.action'} eq 'upload_embedded') {
-            $r->print(&Apache::lonupload::phasethree($r,$fn,$uname,$udom,'testbank'));
+    if (($env{'form.phase'} eq 'four') || ($env{'form.phase'} eq 'three')) {
+        if ($env{'form.phase'} eq 'four') {
+            $r->print(&Apache::lonupload::phasefour($r,$fn,$uname,$udom,'testbank'));
+            my $current_page = 0; 
+            my $js;
+            &jscript_zero($webpath,\$js);
+            $js = '<script type="text/javascript">'."\n$js\n".'</script>';
+            $r->print($js);
+            &display_zero($r,$uname,$fn,$current_page,$webpath);
+        } elsif ($env{'form.phase'} eq 'three') {
+            if ($env{'form.action'} eq 'upload_embedded') {
+                my ($result,$flag) = 
+                     &Apache::lonupload::phasethree($r,$fn,$uname,$udom,'testbank');
+                $r->print($result);
+                if ($flag eq 'modify_orightml') {
+                    undef($page_name); 
+                    $r->print('<form name="testbankForm" method="post" action="/adm/testbank">'.
+                              &page_footer('',$uname,$fn).'</form>');
+                }
+            }
         }
         if ($badfile) {
             &file_error($r,$uname,$fn,$current_page,$webpath,$res);
-        } else {        
+        } else {
             &display_zero ($r,$uname,$fn,$current_page,$webpath) if $page_name eq 'Welcome';
             &display_one ($r,$uname,$fn,$current_page,$textref,$header) if $page_name eq 'Blocks';
             &display_two ($r,$uname,$fn,$current_page,$textref,$header,$qcount) if $page_name eq 'Format';
@@ -1925,7 +1942,6 @@
         my ($result,$flag) = &Apache::lonupload::phasetwo($r,$fn,$uname,$udom,'testbank');
         $r->print($result);
         if ($flag eq 'ok') {
-            my $current_page = 0;
             my $js;
             &jscript_zero($webpath,\$js);
             $js = '<script type="text/javascript">'."\n$js\n".'</script>';
Index: loncom/publisher/lonupload.pm
diff -u loncom/publisher/lonupload.pm:1.50 loncom/publisher/lonupload.pm:1.51
--- loncom/publisher/lonupload.pm:1.50	Wed Oct 27 01:04:15 2010
+++ loncom/publisher/lonupload.pm	Sun Nov 28 00:04:10 2010
@@ -2,7 +2,7 @@
 # The LearningOnline Network with CAPA
 # Handler to upload files into construction space
 #
-# $Id: lonupload.pm,v 1.50 2010/10/27 01:04:15 raeburn Exp $
+# $Id: lonupload.pm,v 1.51 2010/11/28 00:04:10 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -70,7 +70,7 @@
 
 =item *
 
-output relevant interface phase (phaseone or phasetwo or phasethree)
+output relevant interface phase (phaseone, phasetwo, phasethree or phasefour)
 
 =item *
 
@@ -99,6 +99,11 @@
 Interface for handling secondary uploads of embedded objects
 in an html file.
 
+=item phasefour()
+
+Interface for handling optional renaming of links to embedded
+objects. 
+
 =item upfile_store()
 
 Store contents of uploaded file into temporary space.  Invoked
@@ -351,25 +356,32 @@
             &Apache::lonnet::extract_embedded_items($target,\%allfiles,\%codebase);
             if (keys(%allfiles) > 0) {
                 my ($currentpath) = ($url =~ m{^(.+)/[^/]+$});
-                my $state = <<STATE;
-    <input type="hidden" name="action"      value="upload_embedded" />
-    <input type="hidden" name="mode"        value="$mode" />
-    <input type="hidden" name="phase"       value="three" />
-    <input type="hidden" name="filename" value="$url" />
-STATE
-                my $embedded = &Apache::loncommon::ask_for_embedded_content($action,$state,\%allfiles,\%codebase,
-                                      {'error_on_invalid_names'   => 1,
-                                       'ignore_remote_references' => 1,
-                                       'current_path'             => $currentpath});
+                my $state = &embedded_form_elems('upload_embedded',$url,$mode);
+                my ($embedded,$num,$pathchg) = 
+                    &Apache::loncommon::ask_for_embedded_content($action,$state,\%allfiles,
+                                                                 \%codebase,
+                                                                 {'error_on_invalid_names'   => 1,
+                                                                  'ignore_remote_references' => 1,
+                                                                  'current_path'             => $currentpath});
                 if ($embedded) {
-                    $result .= '<h3>'.&mt('Reference Warning').'</h3>'.
-                               '<p>'.&mt('Completed upload of the file. This file contained references to other files.').'</p>'.
-                               '<p>'.&mt('Please select the locations from which the referenced files are to be uploaded.').'</p>'.
-                               $embedded;
-                }
-                if ($mode eq 'testbank') {
-                    $returnflag = 'embedded';
-                    $result .=  '<p>'.&mt('Or [_1]continue[_2] the testbank import without these files','<a href="javascript:document.testbankForm.submit();">','</a>').'</p>';
+                    $result .= '<h3>'.&mt('Reference Warning').'</h3>';
+                    if ($num) {
+                        $result .= '<p>'.&mt('Completed upload of the file.').' '.&mt('This file contained references to other files.').'</p>'.
+                                   '<p>'.&mt('Please select the locations from which the referenced files are to be uploaded.').'</p>'.
+                                   $embedded;
+                        if ($mode eq 'testbank') {
+                            $returnflag = 'embedded';
+                            $result .=  '<p>'.&mt('Or [_1]continue[_2] the testbank import without these files.','<a href="javascript:document.testbankForm.submit();">','</a>').'</p>';
+                        }
+                    } else {
+                        $result .= '<p>'.&mt('Completed upload of the file.').'</p>'.$embedded;
+                        if ($pathchg) {
+                            if ($mode eq 'testbank') {
+                                $returnflag = 'embedded';
+                                $result .=  '<p>'.&mt('Or [_1]continue[_2] the testbank import without modifying the references(s).','<a href="javascript:document.testbankForm.submit();">','</a>').'</p>';
+                            }
+                        }
+                    }
                 }
             }
         }
@@ -389,18 +401,59 @@
 
 sub phasethree {
     my ($r,$fn,$uname,$udom,$mode) = @_;
+    my $action = '/adm/upload'; 
+    if ($mode eq 'testbank') {
+        $action = '/adm/testbank';
+    } elsif ($mode eq 'imsimport') {
+        $action = '/adm/imsimport';
+    }
+    my $dir_root = '/home/'.$uname.'/public_html';
+    my $url_root = '/priv/'.$uname;
+    my $path = &File::Basename::dirname($fn);
+    my $filename = &HTML::Entities::encode($env{'form.filename'},'<>&"');
+    my $state = &embedded_form_elems('modify_orightml',$filename,$mode).
+                '<input type="hidden" name="phase" value="four" />';
+    my ($result,$returnflag) = 
+        &Apache::loncommon::upload_embedded($mode,$path,$uname,$udom,
+                                            $dir_root,$url_root,undef,
+                                            undef,undef,$state,$action);
+    if ($mode ne 'imsimport' && $mode ne 'testbank') {
+        $result .= '<br /><h3><a href="'.$url_root.$fn.'">'.
+                  &mt('View main file').'</a></h3>'.
+                  '<h3><a href="'.$url_root.$path.'">'.
+                  &mt('Back to Directory').'</a></h3><br />';
+    }
+    return ($result,$returnflag);
+}
+
+sub embedded_form_elems {
+    my ($action,$filename,$mode) = @_;
+    return <<STATE;
+    <input type="hidden" name="action" value="$action" />
+    <input type="hidden" name="mode" value="$mode" />
+    <input type="hidden" name="filename" value="$filename" />
+STATE
+}
+
+sub phasefour {
+    my ($r,$fn,$uname,$udom,$mode) = @_;
+    my $action = '/adm/upload';
+    if ($mode eq 'testbank') {
+        $action = '/adm/testbank';
+    } elsif ($mode eq 'imsimport') {
+        $action = '/adm/imsimport';
+    }
     my $result;
     my $dir_root = '/home/'.$uname.'/public_html';
     my $url_root = '/priv/'.$uname;
-    my $base = &File::Basename::basename($fn);
     my $path = &File::Basename::dirname($fn);
-    $result = &Apache::loncommon::upload_embedded($mode,$path,$uname,$udom,
-                                                  $dir_root,$url_root);
+    $result .= &Apache::loncommon::modify_html_refs($mode,$path,
+                              $uname,$udom,$dir_root);
     if ($mode ne 'imsimport' && $mode ne 'testbank') {
-        $result = '<br /><font size="+2"><a href="'.$url_root.$fn.'">'.
-                  &mt('View main file').'</a></font>'.
-                  '<br /><font size="+2"><a href="'.$url_root.$path.'">'.
-                  &mt('Back to Directory').'</a></font><br />';
+        $result .= '<br /><h3><a href="'.$url_root.$fn.'">'.
+                  &mt('View main file').'</a></h3>'.
+                  '<h3><a href="'.$url_root.$path.'">'.
+                  &mt('Back to Directory').'</a></h3><br />';
     }
     return $result;
 }
@@ -490,8 +543,10 @@
                  .'</p>'
         );
     }
-
-    if ($env{'form.phase'} eq 'three') {
+    if ($env{'form.phase'} eq 'four') {
+        my $output = &phasefour($r,$fn,$uname,$udom,'author');
+        $r->print($output);
+    } elsif ($env{'form.phase'} eq 'three') {
         my $output = &phasethree($r,$fn,$uname,$udom,'author');
         $r->print($output);
     } elsif ($env{'form.phase'} eq 'two') {

--raeburn1290902651--