[LON-CAPA-cvs] cvs: loncom /interface loncommon.pm

raeburn raeburn at source.lon-capa.org
Thu Apr 5 09:32:15 EDT 2012


raeburn		Thu Apr  5 13:32:15 2012 EDT

  Modified files:              
    /loncom/interface	loncommon.pm 
  Log:
  Extraction of contents of archive files (zip, tar etc.) from a file
  uploaded directly to a course.  
  - Option for permanent removal of archive file offered later in process.
  - Preview of archive file contents offerered.
  - Where decompression of archive would overwrite an existing file,
    option offered to overwrite existing file, or discard file in the archive
    during extraction.
  
  
-------------- next part --------------
Index: loncom/interface/loncommon.pm
diff -u loncom/interface/loncommon.pm:1.1064 loncom/interface/loncommon.pm:1.1065
--- loncom/interface/loncommon.pm:1.1064	Wed Apr  4 23:06:52 2012
+++ loncom/interface/loncommon.pm	Thu Apr  5 13:32:15 2012
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.1064 2012/04/04 23:06:52 raeburn Exp $
+# $Id: loncommon.pm,v 1.1065 2012/04/05 13:32:15 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -7022,12 +7022,12 @@
             $scope .= "/$env{'request.course.sec'}";
             if ((&Apache::lonnet::allowed('pav',$scope)) ||
                 (&Apache::lonnet::allowed('pfo',$scope))) {
-                return; 
+                return;
             }
         }
         my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
         my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
-        my $blocked = &blocking_status('printout',$cnum,$cdom); 
+        my $blocked = &blocking_status('printout',$cnum,$cdom);
         if ($blocked) {
             my $checkrole = "cm./$cdom/$cnum";
             if ($env{'request.course.sec'} ne '') {
@@ -9928,21 +9928,92 @@
 }
 
 sub decompress_form {
-    my ($mimetype,$archiveurl,$action,$noextract,$hiddenelements) = @_;
+    my ($mimetype,$archiveurl,$action,$noextract,$hiddenelements,$dirlist) = @_;
     my %lt = &Apache::lonlocal::texthash (
         this => 'This file is an archive file.',
+        itsc => 'Its contents are as follows:',
         youm => 'You may wish to extract its contents.',
         camt => 'Extraction of contents is recommended for Camtasia zip files.',
-        perm => 'Permanently remove archive file after extraction of contents?',
         extr => 'Extract contents',
         yes  => 'Yes',
         no   => 'No',
     );
-    my $output = '<p>'.$lt{'this'}.' '.$lt{'youm'}.'<br />';
+    my $output = '<p>'.$lt{'this'};
+    my $fileloc = &Apache::lonnet::filelocation(undef,$archiveurl);
+    my (%toplevel, at paths);
+    my $info = &list_archive_contents($fileloc,\@paths);
+    if (@paths) {
+        foreach my $path (@paths) {
+            $path =~ s{^/}{};
+            if ($path =~ m{^([^/]+)/}) {
+                $toplevel{$1} = $path;
+            } else {
+                $toplevel{$path} = $path;
+            }
+        }
+    }
+    if ($info eq '') {
+        $output .= ' '.$lt{'youm'}.'</p>'."\n";
+    } else {
+        $output .= ' '.$lt{'itsc'}.'</p>'."\n".
+                   '<div><pre>'.$info.'</pre></div>';
+    }
+    my $duplicates;
+    my $num = 0;
+    if (ref($dirlist) eq 'ARRAY') {
+        foreach my $item (@{$dirlist}) {
+            if (ref($item) eq 'ARRAY') {
+                if (exists($toplevel{$item->[0]})) {
+                    $duplicates .= 
+                        &start_data_table_row().
+                        '<td><label><input type="radio" name="archive_overwrite_'.$num.'" '.
+                        'value="0" checked="checked" />'.&mt('No').'</label>'.
+                        ' <label><input type="radio" name="archive_overwrite_'.$num.'" '.
+                        'value="1" />'.&mt('Yes').'</label>'.
+                        '<input type="hidden" name="archive_overwrite_name_'.$num.'" value="'.$item->[0].'" /></td>'."\n".
+                        '<td>'.$item->[0].'</td>';
+                    if ($item->[2]) {
+                        $duplicates .= '<td>'.&mt('Directory').'</td>';
+                    } else {
+                        $duplicates .= '<td>'.&mt('File').'</td>';
+                    }
+                    $duplicates .= '<td>'.$item->[3].'</td>'.
+                                   '<td>'.
+                                   &Apache::lonlocal::locallocaltime($item->[4]).
+                                   '</td>'.
+                                   &end_data_table_row();
+                    $num ++;
+                }
+            }
+        }
+    }
+    my $itemcount;
+    if (@paths > 0) {
+        $itemcount = scalar(@paths);
+    } else {
+        $itemcount = 1;
+    }
+    $output .= 
+        '<input type="hidden" name="archive_overwrite_total" value="'.$num.'" />'.
+        '<input type="hidden" name="archive_itemcount" value="'.$itemcount.'" />'."\n";
+    if ($duplicates ne '') {
+        $output .= '<p><span class="LC_warning">'.
+                   &mt('Warning: decompression of the archive will overwrite the following items which already exist:').'</span><br />'.  
+                   &start_data_table().
+                   &start_data_table_header_row().
+                   '<th>'.&mt('Overwrite?').'</th>'.
+                   '<th>'.&mt('Name').'</th>'.
+                   '<th>'.&mt('Type').'</th>'.
+                   '<th>'.&mt('Size').'</th>'.
+                   '<th>'.&mt('Last modified').'</th>'.
+                   &end_data_table_header_row().
+                   $duplicates.
+                   &end_data_table().
+                   '</p>';
+    }
     if ($mimetype =~ m{^application/(x\-)?(compressed|zip)}) {
-        $output .= $lt{'camt'};
+        $output .= '<p>'.$lt{'camt'}.'</p>';
     }
-    $output .= '</p>';
     $output .= <<"START";
 <div id="uploadfileresult">
   <form name="uploaded_decompress" action="$action" method="post">
@@ -9954,9 +10025,6 @@
         }
     }
     $output .= <<"END";
-<span class="LC_nobreak">$lt{'perm'} 
-<label><input type="radio" name="archivedelete" value="0" checked="checked" />$lt{'no'}</label>  
-<label><input type="radio" name="archivedelete" value="1" />$lt{'yes'}</label></span><br />
 <input type="submit" name="decompress" value="$lt{'extr'}" />
 </form>
 $noextract
@@ -9965,6 +10033,62 @@
     return $output;
 }
 
+sub decompression_utility {
+    my ($program) = @_;
+    my @utilities = ('tar','gunzip','bunzip2','unzip'); 
+    my $location;
+    if (grep(/^\Q$program\E$/, at utilities)) { 
+        foreach my $dir ('/bin/','/usr/bin/','/usr/local/bin/','/sbin/',
+                         '/usr/sbin/') {
+            if (-x $dir.$program) {
+                $location = $dir.$program;
+                last;
+            }
+        }
+    }
+    return $location;
+}
+
+sub list_archive_contents {
+    my ($file,$pathsref) = @_;
+    my (@cmd,$output);
+    my $needsregexp;
+    if ($file =~ /\.zip$/) {
+        @cmd = (&decompression_utility('unzip'),"-l");
+        $needsregexp = 1;
+    } elsif (($file =~ m/\.tar\.gz$/) ||
+             ($file =~ /\.tgz$/)) {
+        @cmd = (&decompression_utility('tar'),"-ztf");
+    } elsif ($file =~ /\.tar\.bz2$/) {
+        @cmd = (&decompression_utility('tar'),"-jtf");
+    } elsif ($file =~ m|\.tar$|) {
+        @cmd = (&decompression_utility('tar'),"-tf");
+    }
+    if (@cmd) {
+        undef($!);
+        undef($@);
+        if (open(my $fh,"-|", @cmd, $file)) {
+            while (my $line = <$fh>) {
+                $output .= $line;
+                chomp($line);
+                my $item;
+                if ($needsregexp) {
+                    ($item) = ($line =~ /^\s*\d+\s+[\d\-]+\s+[\d:]+\s*(.+)$/); 
+                } else {
+                    $item = $line;
+                }
+                if ($item ne '') {
+                    unless (grep(/^\Q$item\E$/,@{$pathsref})) {
+                        push(@{$pathsref},$item);
+                    } 
+                }
+            }
+            close($fh);
+        }
+    }
+    return $output;
+}
+
 sub decompress_uploaded_file {
     my ($file,$dir) = @_;
     &Apache::lonnet::appenv({'cgi.file' => $file});
@@ -9994,8 +10118,6 @@
         } else {
             my @ids=&Apache::lonnet::current_machine_ids();
             my $currdir = "$dir_root/$destination";
-            my ($currdirlistref,$currlisterror) =
-                &Apache::lonnet::dirlist($currdir,$docudom,$docuname,1);
             if (grep(/^\Q$docuhome\E$/, at ids)) {
                 $dir = &LONCAPA::propath($docudom,$docuname).
                        "$dir_root/$destination";
@@ -10006,47 +10128,61 @@
                     $error = &mt('Archive file not found.');
                 }
             }
-            if ($dir eq '') {
+            my (@to_overwrite, at to_skip);
+            if ($env{'form.archive_overwrite_total'} > 0) {
+                my $total = $env{'form.archive_overwrite_total'};
+                for (my $i=0; $i<$total; $i++) {
+                    if ($env{'form.archive_overwrite_'.$i} == 1) {
+                        push(@to_overwrite,$env{'form.archive_overwrite_name_'.$i});
+                    } elsif ($env{'form.archive_overwrite_'.$i} == 0) {
+                        push(@to_skip,$env{'form.archive_overwrite_name_'.$i});
+                    }
+                }
+            }
+            my $numskip = scalar(@to_skip);
+            if (($numskip > 0) && 
+                ($numskip == $env{'form.archive_itemcount'})) {
+                $warning = &mt('All items in the archive file already exist, and no overwriting of existing files has been requested.');         
+            } elsif ($dir eq '') {
                 $error = &mt('Directory containing archive file unavailable.');
             } elsif (!$error) {
-                my ($decompressed,$display) = &decompress_uploaded_file($file,$dir);
+                my ($decompressed,$display);
+                if ($numskip > 0) {
+                    my $tempdir = time.'_'.$$.int(rand(10000));
+                    mkdir("$dir/$tempdir",0755);
+                    system("mv $dir/$file $dir/$tempdir/$file");
+                    ($decompressed,$display) = 
+                        &decompress_uploaded_file($file,"$dir/$tempdir");
+                    foreach my $item (@to_skip) {
+                        if (($item ne '') && ($item !~ /\.\./)) {
+                            if (-f "$dir/$tempdir/$item") { 
+                                unlink("$dir/$tempdir/$item");
+                            } elsif (-d "$dir/$tempdir/$item") {
+                                system("rm -rf $dir/$tempdir/$item");
+                            }
+                        }
+                    }
+                    system("mv $dir/$tempdir/* $dir");
+                    rmdir("$dir/$tempdir");   
+                } else {
+                    ($decompressed,$display) = 
+                        &decompress_uploaded_file($file,$dir);
+                }
                 if ($decompressed eq 'ok') {
-                    $output = &mt('Files extracted successfully from archive.').'<br />';
+                    $output = '<p class="LC_info">'.
+                              &mt('Files extracted successfully from archive.').
+                              '</p>'."\n";
                     my ($warning,$result, at contents);
                     my ($newdirlistref,$newlisterror) =
                         &Apache::lonnet::dirlist($currdir,$docudom,
                                                  $docuname,1);
                     my (%is_dir,%changes, at newitems);
                     my $dirptr = 16384;
-                    if (ref($currdirlistref) eq 'ARRAY') {
-                        my @curritems;
-                        foreach my $dir_line (@{$currdirlistref}) {
-                            my ($item,$rest)=split(/\&/,$dir_line,2);
-                            unless ($item =~ /\.+$/) {
-                                push(@curritems,$item);
-                            }
-                        }
-                        if (ref($newdirlistref) eq 'ARRAY') {
-                            foreach my $dir_line (@{$newdirlistref}) {
-                                my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,4);
-                                unless ($item =~ /^\.+$/) {
-                                    if ($dirptr&$testdir) {
-                                        $is_dir{$item} = 1;
-                                    }
-                                    push(@newitems,$item);
-                                }
-                            }
-                            my @diffs = &compare_arrays(\@curritems,\@newitems);
-                            if (@diffs > 0) {
-                               foreach my $item (@diffs) {
-                                   $changes{$item} = 1;
-                               }
-                            }
-                        }
-                    } elsif (ref($newdirlistref) eq 'ARRAY') {
+                    if (ref($newdirlistref) eq 'ARRAY') {
                         foreach my $dir_line (@{$newdirlistref}) {
                             my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
-                            unless ($item =~ /\.+$/) {
+                            unless (($item =~ /^\.+$/) || ($item eq $file) || 
+                                    ((@to_skip > 0) && (grep(/^\Q$item\E$/, at to_skip)))) {
                                 push(@newitems,$item);
                                 if ($dirptr&$testdir) {
                                     $is_dir{$item} = 1;
@@ -10073,7 +10209,7 @@
                         if ($datatable ne '') {
                             $output .= &archive_options_form('decompressed',$datatable,
                                                              $count,$hiddenelem);
-                            my $startcount = 4;
+                            my $startcount = 6;
                             $output .= &archive_javascript($startcount,$count,
                                                            \%titles,\%children);
                         }
@@ -10202,6 +10338,9 @@
     my $offset = 0;
     foreach my $action ('display','dependency','discard') {
         $offset ++;
+        if ($action ne 'display') {
+            $offset ++;
+        }  
         $output .= '<td><span class="LC_nobreak">'.
                    '<label><input type="radio" name="archive_'.$count.
                    '" id="archive_'.$action.'_'.$count.'" value="'.$action.'"';
@@ -10247,27 +10386,47 @@
 }
 
 sub archive_options_form {
-    my ($form,$output,$count,$hiddenelem) = @_;
-    return '<form name="'.$form.'" method="post" action="">'."\n".
-           '<input type="hidden" name="phase" value="decompress_cleanup" />'."\n".
-                    '<p>'.
-                    &mt('How should each item be incorporated in the course?').
-                    '</p>'.
-                    '<div class="LC_columnSection"><fieldset>'.
-                    '<legend>'.&mt('Content actions for all').'</legend>'.
-                    '<input type="button" value="'.&mt('Add as folder/file').'" '.
-                    'onclick="javascript:checkAll(document.'.$form.",'display'".')" />'.
-                    '  <input type="button" value="'.&mt('Include as dependency for a displayed file').'"'.
-                    ' onclick="javascript:checkAll(document.'.$form.",'dependency'".')" />'.
-                    '  <input type="button" value="'.&mt('Discard').'"'.
-                    ' onclick="javascript:checkAll(document.'.$form.",'discard'".')" />'.
-                     '</fieldset></div>'.
+    my ($form,$display,$count,$hiddenelem) = @_;
+    my %lt = &Apache::lonlocal::texthash(
+               perm => 'Permanently remove archive file?',
+               hows => 'How should each extracted item be incorporated in the course?',
+               cont => 'Content actions for all',
+               addf => 'Add as folder/file',
+               incd => 'Include as dependency for a displayed file',
+               disc => 'Discard',
+               no   => 'No',
+               yes  => 'Yes',
+               save => 'Save',
+    );
+    my $output = <<"END";
+<form name="$form" method="post" action="">
+<p><span class="LC_nobreak">$lt{'perm'} 
+<label>
+  <input type="radio" name="archivedelete" value="0" checked="checked" />$lt{'no'}
+</label>
+ 
+<label>
+  <input type="radio" name="archivedelete" value="1" />$lt{'yes'}</label>
+</span>
+</p>
+<input type="hidden" name="phase" value="decompress_cleanup" />
+<br />$lt{'hows'}
+<div class="LC_columnSection">
+  <fieldset>
+    <legend>$lt{'cont'}</legend>
+    <input type="button" value="$lt{'addf'}" onclick="javascript:checkAll(document.$form,'display');" /> 
+      <input type="button" value="$lt{'incd'}" onclick="javascript:checkAll(document.$form,'dependency');" />
+      <input type="button" value="$lt{'disc'}" onclick="javascript:checkAll(document.$form,'discard');" />
+  </fieldset>
+</div>
+END
+    return $output.
            &start_data_table()."\n".
-           $output."\n".
+           $display."\n".
            &end_data_table()."\n".
            '<input type="hidden" name="archive_count" value="'.$count.'" />'.
            $hiddenelem.
-           '<br /><input type="submit" name="archive_submit" value="'.&mt('Save').'" />'.
+           '<br /><input type="submit" name="archive_submit" value="'.$lt{'save'}.'" />'.
            '</form>';
 }
 
@@ -10388,7 +10547,7 @@
 
 function propagateSelect(form,count,offset) {
     if (count > 0) {
-        var item = (2+offset+$startcount)+7*(count-1);
+        var item = (1+offset+$startcount)+7*(count-1);
         var picked = form.elements[item].options[form.elements[item].selectedIndex].value; 
         if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
             if (parents[count].length > 0) {
@@ -10402,7 +10561,7 @@
 
 function containerSelect(form,count,offset,picked) {
     if (count > 0) {
-        var item = (1+offset+$startcount)+7*(count-1);
+        var item = (offset+$startcount)+7*(count-1);
         if (form.elements[item].type == 'radio') {
             if (form.elements[item].value == 'dependency') {
                 if (form.elements[item+1].type == 'select-one') {
@@ -10452,7 +10611,7 @@
 }
 
 sub process_extracted_files {
-    my ($context,$docudom,$docuname,$destination,$dir_root,$hiddenelem) = @_;
+    my ($context,$docudom,$docuname,$url,$destination,$dir_root,$hiddenelem) = @_;
     my $numitems = $env{'form.archive_count'};
     return unless ($numitems);
     my @ids=&Apache::lonnet::current_machine_ids();


More information about the LON-CAPA-cvs mailing list