Skip to content

Commit

Permalink
Item14223: implemented new upload backend
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelDaum committed Nov 22, 2016
1 parent 63a7ac0 commit 4d34fd0
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 136 deletions.
7 changes: 5 additions & 2 deletions lib/Foswiki/Plugins/TopicInteractionPlugin/Action.pm
Expand Up @@ -39,7 +39,10 @@ sub handle {
}

sub prepareAction {
my ($this, $response) = @_;
my ($this, $response, $opts) = @_;

$opts ||= {};
$opts->{requireFileName} = 1 unless defined $opts->{requireFileName};

$this->writeDebug("*** called handleRest()");

Expand Down Expand Up @@ -89,7 +92,7 @@ sub prepareAction {
}

my $fileName = $params->{name} || $params->{filename} || '';
unless ($fileName) {
if ($opts->{requireFileName} && !$fileName) {
$this->printJSONRPC($response, 103, "No filename", $id);
return;
}
Expand Down
194 changes: 74 additions & 120 deletions lib/Foswiki/Plugins/TopicInteractionPlugin/Action/UploadAttachment.pm
Expand Up @@ -22,38 +22,35 @@ use Error qw( :try );
use Foswiki::Func ();
use Foswiki::Plugins::TopicInteractionPlugin::Action ();
our @ISA = ('Foswiki::Plugins::TopicInteractionPlugin::Action');
use constant DRY => 0; # toggle me

sub handle {
my ($this, $response) = @_;

my $params = $this->prepareAction($response);
my $params = $this->prepareAction($response, {
requireFileName => 0
});
return unless $params;

my $web = $params->{web};
my $topic = $params->{topic};
my $id = $params->{id};

($web, $topic) = Foswiki::Func::normalizeWebTopicName($web, $topic);

my ($meta, undef) = Foswiki::Func::readTopic($web, $topic);

# check permissions
my $wikiName = Foswiki::Func::getWikiName();
unless (Foswiki::Func::checkAccessPermission(
'CHANGE', $wikiName, undef, $topic, $web)) {
unless (Foswiki::Func::checkAccessPermission('CHANGE', $wikiName, undef, $topic, $web, $meta)) {
$this->printJSONRPC($response, 102, "Access denied", $id);
return;
}

my $origName = $params->{filename};
my $fileName = $this->sanitizeAttachmentName($origName);
my $maxSize = Foswiki::Func::getPreferencesValue('ATTACHFILESIZELIMIT');
$maxSize = 0 unless ($maxSize =~ /([0-9]+)/);

$this->writeDebug("'$origName' has been renamed to '$fileName'")
unless $fileName eq $origName;

# read additional params
my $request = Foswiki::Func::getCgiQuery();
my $nrChunks = $params->{chunks} || 0;
my $contentType = $request->header('content-type') || '';
my $isMultiPart = ($contentType =~ /multipart/)?1:0;
my $chunk = $params->{chunk} || 0;

my $fileCreateLink = $params->{createlink} || '0';
$fileCreateLink = $fileCreateLink eq 'on' ? 1:0;
Expand All @@ -62,143 +59,100 @@ sub handle {
$fileHide = 'off' unless defined $fileHide;
$fileHide = $fileHide eq 'on' ? 1:0;

my $fileComment = $params->{filecomment};
$fileComment = '' unless defined $fileComment;
# loop thru all uploads
my $uploads = $request->uploads();
my @result = ();
foreach my $fileName (keys %$uploads) {
my $upload = $uploads->{$fileName};

$this->writeDebug("receiving file $fileName, chunk $chunk of $nrChunks, id=$id".($isMultiPart?' in multipart mode':' in normal mode'));
my $tmpFileName = $upload->tmpFileName;
my $origName = $fileName;
$fileName = $this->sanitizeAttachmentName($origName);

# read application/octet-stream, can't use CGI.pm means
my $tmpDir = Foswiki::Func::getWorkArea("TopicInteractionPlugin");
my $tmpFileName = $tmpDir.'/'.$fileName.'_part_'.$id;
my $data = '';

# read data from request, either from a multipart of streamed request
if ($isMultiPart) {
my $stream = $request->upload('file');
unless (defined $stream) {
$this->printJSONRPC($response, 110, "Stream not found for '$fileName'", $id);
return;
}
my $r;
my $transfer;
while ($r = sysread($stream, $transfer, 0x80000)) {
if (!defined $r) {
next if ($! == Errno::EINTR);
$this->printJSONRPC($response, 1, "System read error: $!", $id);
return;
}
$data .= $transfer;
unless ($fileName =~ /\./) {
my $info = $upload->uploadInfo;
my $suffix = $this->getSuffixOfMimeType($info->{"Content-Type"});
$fileName .= ".".$suffix if $suffix;
}
} else {
$data = $request->param("POSTDATA") || '';
}

if (-e $tmpFileName && $chunk <= $nrChunks) {
$this->writeDebug("appending to $tmpFileName");
$this->appendFile($tmpFileName, $data);
} else {
$this->writeDebug("saving to $tmpFileName");
$this->saveFile($tmpFileName, $data);
}

# end of transaction
if ($chunk+1 >= $nrChunks) {
my $newFileName = $tmpFileName;
$newFileName =~ s/_part_.*?$//;
rename $tmpFileName, $newFileName
if $tmpFileName ne $newFileName;

$this->writeDebug("finished uploading $newFileName");
my ($meta, $text) = Foswiki::Func::readTopic($web, $topic);
my $prevHide = '';
my $prevComment = '';;
my $prevAttachment = $meta->get('FILEATTACHMENT', $fileName);
if($prevAttachment) {
$prevComment = $prevAttachment->{comment} || '';
$prevHide = ($prevAttachment->{attr} =~ /h/)?1:0;
$this->writeDebug("prevComment=$prevComment, prevHide=$prevHide");
} else {
$this->writeDebug("no previous FILEATTACHMENT for $fileName");
my $stream = $upload->handle;
my $fileSize;
my $fileDate;
if ($stream) {
my @stats = stat $stream;
$fileSize = $stats[7];
$fileDate = $stats[9];
}

my @stats = stat $newFileName;
my $fileSize = $stats[7] || 0;
my $fileDate = $stats[9] || 0;
$fileComment = $prevComment unless $fileComment;
$fileHide = $prevHide unless $fileHide;

unless ($fileSize) {
$this->printJSONRPC($response, 111, "Zero-sized file upload of '$fileName'", $id);
unless ($fileSize && $fileName) {
$this->printJSONRPC($response, 1, "Zero-sized file upload of '$fileName'", $id);
close($stream) if $stream;
return;
}

# check content length
my $maxSize = Foswiki::Func::getPreferencesValue('ATTACHFILESIZELIMIT');
$maxSize = 0 unless ($maxSize =~ /([0-9]+)/o);
$maxSize =~ s/[^\d]//g;

$this->writeDebug("fileSize=$fileSize, maxSize=$maxSize, fileDate=$fileDate, fileComment=$fileComment, fileHide=$fileHide, fileCreateLink=$fileCreateLink");

if ($maxSize && $fileSize > $maxSize * 1024) {
$this->printJSONRPC($response, 109, "Oversized upload of '$fileName'", $id);
$this->printJSONRPC($response, 1, "Oversized upload of '$fileName'", $id);
close($stream) if $stream;
return;
}

my $fileComment = $params->{filecomment};
unless (defined $fileComment) {
#$this->writeDebug("filecomment not found in query ... reading from topic");
# get prev comment as we override it otherwise
my $attrs = $meta->get( 'FILEATTACHMENT', $fileName );
$fileComment = $attrs->{comment} // '';
}

$this->writeDebug("web=$web, topic=$topic, fileName=$fileName, origName=$origName, tmpFileName=$tmpFileName, fileComment=$fileComment");

my $error;
try {
$this->writeDebug("attaching $fileName to $web.$topic");
unless (DRY) {
$error = Foswiki::Func::saveAttachment(
$web, $topic, $fileName, {
dontlog => !$Foswiki::cfg{Log}{upload},
comment => $fileComment,
hide => $fileHide,
createlink => $fileCreateLink,
file => $newFileName,
filesize => $fileSize, # SMELL: which one is
size => $fileSize, # SMELL: ... correct
filedate => $fileDate,
});
}
$this->writeDebug("removing temp file $newFileName");
unlink $newFileName if -e $newFileName;

sleep(1); # sleep for a while to prevent a firing hurdle of events on save handlers in other extensions

$error = Foswiki::Func::saveAttachment(
$web, $topic, $fileName, {
dontlog => !$Foswiki::cfg{Log}{upload},
comment => $fileComment,
stream => $stream,
filesize => $fileSize,
filedate => $fileDate,
tmpFilename => $tmpFileName,
});
} catch Error::Simple with {
$error = shift->{-text};
$this->writeError("ERROR: $error");
};

if ($error) {
$this->printJSONRPC($response, 1, $error, $id);
close($stream) if $stream;
return;
}

close($stream) if $stream;
push @result, {
origName => $origName,
fileName => $fileName
};
}

$this->printJSONRPC($response, 0, {$origName => $fileName}, $id);
$this->printJSONRPC($response, 0, \@result, $id);
}

sub appendFile {
my ($this, $name, $text) = @_;
my $FILE;
unless (open($FILE, '>>', $name)) {
die "Can't append to $name - $!\n";
sub getSuffixOfMimeType {
my ($this, $mimeType) = @_;

my $suffix;

unless ($this->{types}) {
$this->{types} = Foswiki::Func::readFile($Foswiki::cfg{MimeTypesFileName});
}
binmode($FILE);
print $FILE $text;
close($FILE);
}

sub saveFile {
my ($this, $name, $text) = @_;
my $FILE;
unless (open($FILE, '>', $name)) {
die "Can't create file $name - $!\n";
if ($this->{types} =~ /^$mimeType\s+(\S+)\s*/im) {
$suffix = $1;
$this->writeDebug("getSuffixOfMimeType($mimeType) = $suffix");
}
binmode($FILE);
print $FILE $text;
close($FILE);

return $suffix;
}


1;
Expand Up @@ -54,15 +54,15 @@ As per the GPL, removal of this notice is prohibited.
}

args = $.makeArray(arguments);
args.unshift("FoswikiAttachments:");
args.unshift("FA:");
console.log.apply(console, args);
};

/* init attachments *****************************************************/
FoswikiAttachments.prototype.init = function () {
var self = this, tabpane;

//self.log("called init()");
self.log("called init()");

self.container = self.elem.parent();
self.optionsButton = self.elem.find(".foswikiAttachmentsOptionsToggle");
Expand Down Expand Up @@ -98,9 +98,9 @@ As per the GPL, removal of this notice is prohibited.

// add listener for refresh event
self.elem.bind("refresh", function(e, files) {
//self.log("got refresh event");
self.log("got refresh event");
if (files) {
//self.log("refreshing from files");
self.log("refreshing from files",files);
self.clearSelection();
$.each(files, function(i, file) {
if (typeof(file) === 'string') {
Expand Down Expand Up @@ -701,6 +701,8 @@ As per the GPL, removal of this notice is prohibited.
var self = this,
url, thisParams = {};

self.log("called load()");

$.each(params, function(key, val) {
thisParams["attachments_"+key] = val;
});
Expand All @@ -722,7 +724,7 @@ As per the GPL, removal of this notice is prohibited.
}, thisParams);

$.each(self.selection, function(i, item) {
params["attachments_selection"].push(item.replace(/\\\./, '.'));
params["attachments_selection"].push(encodeURIComponent(item.replace(/\\\./, '.')));
});

//self.log("params=",params);
Expand Down
Expand Up @@ -37,7 +37,7 @@
dropZone.appendTo("body");

self.fileElem.fileupload({
url: foswiki.getScriptUrl("rest", "JQFileUploadPlugin", "upload", {
url: foswiki.getScriptUrl("rest", "TopicInteractionPlugin", "upload", {
topic: self.opts.topic
}),
dataType: 'json',
Expand All @@ -51,6 +51,7 @@
},
add: function(e, data) {
data.formData = self.opts;
data.formData.id = Math.ceil(Math.random()*1000);
data.submit();
},
start: function() {
Expand Down Expand Up @@ -87,7 +88,8 @@
self.dragoverTimer = null;
$("body").removeClass("jqFileUploadDragging");
},
done: function(e, data) {
done: function(e, xhr) {
var data = xhr.result;
$.map(data.result, function(val) {
self.uploadedFiles.push(val.fileName);
});
Expand Down

0 comments on commit 4d34fd0

Please sign in to comment.