[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