aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorzotlabs <mike@macgirvin.com>2017-07-13 17:04:58 -0700
committerzotlabs <mike@macgirvin.com>2017-07-13 17:04:58 -0700
commitc940d8d7ca25691519d400ad19aa22b5611fc2e7 (patch)
treeb33805ab6561bc312845be52c9eb0a141e60912a
parentaa70cbbf21950e37ab9b5198de76ded0f1c8351d (diff)
downloadvolse-hubzilla-c940d8d7ca25691519d400ad19aa22b5611fc2e7.tar.gz
volse-hubzilla-c940d8d7ca25691519d400ad19aa22b5611fc2e7.tar.bz2
volse-hubzilla-c940d8d7ca25691519d400ad19aa22b5611fc2e7.zip
implement chunked uploads on the wall; making it work painlessly on /cloud was attempted but will not be implemented today. That presents some interesting dragons to slay.
-rw-r--r--Zotlabs/Module/Wall_attach.php64
-rw-r--r--include/attach.php101
-rwxr-xr-xview/tpl/jot-header.tpl172
-rwxr-xr-xview/tpl/jot.tpl3
4 files changed, 206 insertions, 134 deletions
diff --git a/Zotlabs/Module/Wall_attach.php b/Zotlabs/Module/Wall_attach.php
index c6fe7518e..5b3768da9 100644
--- a/Zotlabs/Module/Wall_attach.php
+++ b/Zotlabs/Module/Wall_attach.php
@@ -8,10 +8,21 @@ require_once('include/photos.php');
class Wall_attach extends \Zotlabs\Web\Controller {
+ function init() {
+ logger('request_method: ' . $_SERVER['REQUEST_METHOD'],LOGGER_DATA,LOG_INFO);
+ logger('wall_attach: ' . print_r($_REQUEST,true),LOGGER_DEBUG,LOG_INFO);
+ logger('wall_attach files: ' . print_r($_FILES,true),LOGGER_DEBUG,LOG_INFO);
+ // for testing without actually storing anything
+ // http_status_exit(200,'OK');
+ }
+
+
function post() {
$using_api = false;
-
+
+ $result = [];
+
if($_REQUEST['api_source'] && array_key_exists('media',$_FILES)) {
$using_api = true;
}
@@ -28,7 +39,44 @@ class Wall_attach extends \Zotlabs\Web\Controller {
if(! $channel)
killme();
-
+
+ $matches = [];
+ $partial = false;
+
+ $x = preg_match('/bytes (\d*)\-(\d*)\/(\d*)/',$_SERVER['HTTP_CONTENT_RANGE'],$matches);
+ if($x) {
+ // logger('Content-Range: ' . print_r($matches,true));
+ $partial = true;
+ }
+
+ if($partial) {
+ $x = save_chunk($channel,$matches[1],$matches[2],$matches[3]);
+ if($x['partial']) {
+ header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0));
+ json_return_and_die($result);
+ }
+ else {
+ $_FILES['userfile'] = [
+ 'name' => $x['name'],
+ 'type' => $x['type'],
+ 'tmp_name' => $x['tmp_name'],
+ 'error' => $x['error'],
+ 'size' => $x['size']
+ ];
+ }
+ }
+ else {
+ if(! array_key_exists('userfile',$_FILES)) {
+ $_FILES['userfile'] = [
+ 'name' => $_FILES['files']['name'],
+ 'type' => $_FILES['files']['type'],
+ 'tmp_name' => $_FILES['files']['tmp_name'],
+ 'error' => $_FILES['files']['error'],
+ 'size' => $_FILES['files']['size']
+ ];
+ }
+ }
+
$observer = \App::get_observer();
@@ -51,10 +99,14 @@ class Wall_attach extends \Zotlabs\Web\Controller {
if($using_api)
return $s;
-
- echo $s;
- killme();
-
+
+
+ if($partial)
+ header('Range: bytes=0-' . (($x['length']) ? $x['length'] - 1 : 0));
+ $result['message'] = $s;
+ json_return_and_die($result);
+
}
+
}
diff --git a/include/attach.php b/include/attach.php
index ac50b05b1..3a0c8b7ba 100644
--- a/include/attach.php
+++ b/include/attach.php
@@ -2355,3 +2355,104 @@ function attach_upgrade() {
}
}
+
+function save_chunk($channel,$start,$end,$len) {
+
+ $result = [];
+
+ $tmp_path = $_FILES['files']['tmp_name'];
+ $new_base = 'store/[data]/' . $channel['channel_address'] . '/tmp';
+ os_mkdir($new_base,STORAGE_DEFAULT_PERMISSIONS,true);
+
+ $new_path = $new_base . '/' . $_FILES['files']['name'];
+
+ if(! file_exists($new_path)) {
+ rename($tmp_path,$new_path);
+ }
+ else {
+ $istream = fopen($tmp_path,'rb');
+ $ostream = fopen($new_path,'ab');
+ if($istream && $ostream) {
+ pipe_streams($istream,$ostream);
+ fclose($istream);
+ fclose($ostream);
+ }
+ }
+ if(($len - 1) == $end) {
+ unlink($tmp_path);
+ $result['name'] = $_FILES['files']['tmp_name'];
+ $result['type'] = $_FILES['files']['type'];
+ $result['tmp_name'] = $new_path;
+ $result['error'] = 0;
+ $result['size'] = $len;
+ $result['complete'] = true;
+ return $result;
+ }
+ $result['partial'] = true;
+ $result['length'] = intval(filesize($new_path));
+ return $result;
+}
+
+
+/*
+ * chunkloader
+ * Submit handler for chunked uploads
+ *
+ */
+
+function chunkloader($channel,$arr) {
+
+ logger('request: ' . print_r($arr,true), LOGGER_DEBUG);
+ logger('files: ' . print_r($_FILES,true), LOGGER_DEBUG);
+
+
+ $result = [];
+
+
+ $tmp_path = $_FILES['file']['tmp_name'];
+ $new_base = 'store/[data]/' . $channel['channel_address'] . '/tmp';
+ os_mkdir($new_base,STORAGE_DEFAULT_PERMISSIONS,true);
+
+ $new_path = $new_base . '/' . $arr['resumableFilename'];
+
+ rename($tmp_path,$new_path . '.' . intval($arr['resumableChunkNumber']));
+
+ $missing_parts = false;
+ for($x = 1; $x <= intval($arr['resumableTotalChunks']); $x ++) {
+ if(! file_exists($new_path . '.' . $x)) {
+ $missing_parts = true;
+ break;
+ }
+ }
+
+ if($missing_parts) {
+ $result['partial'] = true;
+ return $result;
+ }
+
+ if(intval($arr['resumableTotalChunks']) === 1) {
+ rename($new_path . '.' . '1', $new_path);
+ }
+ else {
+ for($x = 1; $x <= intval($arr['resumableTotalChunks']); $x ++) {
+ $istream = fopen($new_path . '.' . $x,'rb');
+ $ostream = fopen($new_path,'ab');
+ if($istream && $ostream) {
+ pipe_streams($istream,$ostream);
+ fclose($istream);
+ fclose($ostream);
+ }
+ unlink($new_path . '.' . $x);
+ }
+ }
+
+ $result['name'] = $arr['resumableFilename'];
+ $result['type'] = $arr['resumableType'];
+ $result['tmp_name'] = $new_path;
+ $result['error'] = 0;
+ $result['size'] = $arr['resumableTotalSize'];
+ $result['complete'] = true;
+ return $result;
+
+}
+
diff --git a/view/tpl/jot-header.tpl b/view/tpl/jot-header.tpl
index 1cc5a898a..d8f296aa4 100755
--- a/view/tpl/jot-header.tpl
+++ b/view/tpl/jot-header.tpl
@@ -100,77 +100,44 @@ function enableOnUser(){
initEditor();
}
</script>
-<script type="text/javascript" src="{{$baseurl}}/view/js/ajaxupload.js" ></script>
+
+<script src="library/blueimp_upload/js/vendor/jquery.ui.widget.js"></script>
+<script src="library/blueimp_upload/js/jquery.iframe-transport.js"></script>
+<script src="library/blueimp_upload/js/jquery.fileupload.js"></script>
+
<script>
$(document).ready(function() {
/* enable tinymce on focus and click */
$("#profile-jot-text").focus(enableOnUser);
$("#profile-jot-text").click(enableOnUser);
- var upload_title = $('#wall-image-upload').attr('title');
- var attach_title = $('#wall-file-upload').attr('title');
- try {
- var uploader = new window.AjaxUpload('wall-image-upload',
- { action: '{{$baseurl}}/wall_upload/{{$nickname}}',
- name: 'userfile',
- title: upload_title,
- onSubmit: function(file,ext) { $('#profile-rotator').spin('tiny'); },
- onComplete: function(file,response) {
- addeditortext(response);
- $('#jot-media').val($('#jot-media').val() + response);
- $('#profile-rotator').spin(false);
- }
- });
- } catch (e) {
- }
- try {
- var uploader_sub = new window.AjaxUpload('wall-image-upload-sub',
- { action: '{{$baseurl}}/wall_upload/{{$nickname}}',
- name: 'userfile',
- title: upload_title,
- onSubmit: function(file,ext) { $('#profile-rotator').spin('tiny'); },
- onComplete: function(file,response) {
- addeditortext(response);
- $('#jot-media').val($('#jot-media').val() + response);
- $('#profile-rotator').spin(false);
- }
- });
- } catch(e) {
- }
- try {
- var file_uploader = new window.AjaxUpload('wall-file-upload',
- { action: '{{$baseurl}}/wall_attach/{{$nickname}}',
- name: 'userfile',
- title: attach_title,
- onSubmit: function(file,ext) { $('#profile-rotator').spin('tiny'); },
- onComplete: function(file,response) {
- addeditortext(response);
- $('#jot-media').val($('#jot-media').val() + response);
- $('#profile-rotator').spin(false);
- }
- });
- } catch(e) {
- }
- try {
- var file_uploader_sub = new window.AjaxUpload('wall-file-upload-sub',
- { action: '{{$baseurl}}/wall_attach/{{$nickname}}',
- name: 'userfile',
- title: attach_title,
- onSubmit: function(file,ext) { $('#profile-rotator').spin('tiny'); },
- onComplete: function(file,response) {
- addeditortext(response);
- $('#jot-media').val($('#jot-media').val() + response);
- $('#profile-rotator').spin(false);
- }
- });
- } catch(e) {
- }
-
-
+
+ $('#invisible-wall-file-upload').fileupload({
+ url: 'wall_attach/{{$nickname}}',
+ dataType: 'json',
+ dropZone: $('#profile-jot-text'),
+ maxChunkSize: 4 * 1024 * 1024,
+ add: function(e,data) {
+ $('#profile-rotator').spin('tiny');
+ data.submit();
+ },
+ done: function(e,data) {
+ addeditortext(data.result.message);
+ $('#jot-media').val($('#jot-media').val() + data.result.message);
+ },
+ stop: function(e,data) {
+ $('#profile-rotator').spin(false);
+ },
+ });
+
+ $('#wall-file-upload').click(function(event) { event.preventDefault(); $('#invisible-wall-file-upload').trigger('click'); return false;});
+ $('#wall-file-upload-sub').click(function(event) { event.preventDefault(); $('#invisible-wall-file-upload').trigger('click'); return false;});
+
// call initialization file
if (window.File && window.FileList && window.FileReader) {
DragDropUploadInit();
}
+
});
function deleteCheckedItems() {
@@ -291,15 +258,17 @@ function enableOnUser(){
function linkdrop(event) {
var reply = event.dataTransfer.getData("text/uri-list");
- event.preventDefault();
- var editwin = '#' + event.target.id;
- var commentwin = false;
- if(editwin) {
- commentwin = ((editwin.indexOf('comment') >= 0) ? true : false);
- if(commentwin) {
- var commentid = editwin.substring(editwin.lastIndexOf('-') + 1);
- commentOpen(document.getElementById(event.target.id),commentid);
+ if(reply) {
+ event.preventDefault();
+ var editwin = '#' + event.target.id;
+ var commentwin = false;
+ if(editwin) {
+ commentwin = ((editwin.indexOf('comment') >= 0) ? true : false);
+ if(commentwin) {
+ var commentid = editwin.substring(editwin.lastIndexOf('-') + 1);
+ commentOpen(document.getElementById(event.target.id),commentid);
+ }
}
}
@@ -353,8 +322,6 @@ function enableOnUser(){
commentBusy = true;
$('body').css('cursor', 'wait');
$.get('{{$baseurl}}/filer/' + id + '?term=' + reply, NavUpdate);
-// if(timer) clearTimeout(timer);
-// timer = setTimeout(NavUpdate,3000);
liking = 1;
$('#item-filer-dialog').modal('hide');
}
@@ -499,32 +466,20 @@ function enableOnUser(){
};
//
- // initialize
+ // initialize drag-drop
function DragDropUploadInit() {
var filedrag = $("#profile-jot-text");
- // is XHR2 available?
- var xhr = new XMLHttpRequest();
- if (xhr.upload) {
-
- // file drop
+ // file drop
filedrag.on("dragover", DragDropUploadFileHover);
filedrag.on("dragleave", DragDropUploadFileHover);
filedrag.on("drop", DragDropUploadFileSelectHandler);
- }
-
- window.filesToUpload = 0;
- window.fileUploadsCompleted = 0;
-
-
}
// file drag hover
function DragDropUploadFileHover(e) {
- e.stopPropagation();
- e.preventDefault();
e.target.className = (e.type == "dragover" ? "hover" : "");
}
@@ -533,49 +488,12 @@ function enableOnUser(){
// cancel event and hover styling
DragDropUploadFileHover(e);
- if (!editor) $("#profile-jot-text").val("");
-
-
- // fetch FileList object
- var files = e.target.files || e.originalEvent.dataTransfer.files;
- // process all File objects
- for (var i = 0, f; f = files[i]; i++) {
- DragDropUploadFile(f, i);
- }
-
- }
-
- // upload files
- function DragDropUploadFile(file, idx) {
-
- window.filesToUpload = window.filesToUpload + 1;
-
- var xhr = new XMLHttpRequest();
- xhr.withCredentials = true; // Include the SESSION cookie info for authentication
- (xhr.upload || xhr).addEventListener('progress', function (e) {
- $('#profile-rotator').spin('tiny');
- });
- xhr.addEventListener('load', function (e) {
- //console.log('xhr upload complete', e);
- window.fileUploadsCompleted = window.fileUploadsCompleted + 1;
-
- initEditor(function() {
- addeditortext(xhr.responseText);
- });
-
- $('#jot-media').val($('#jot-media').val() + xhr.responseText);
- // When all the uploads have completed, refresh the page
- if (window.filesToUpload > 0 && window.fileUploadsCompleted === window.filesToUpload) {
- $('#profile-rotator').spin(false);
- window.fileUploadsCompleted = window.filesToUpload = 0;
- }
- });
- // POST to the wall_upload endpoint
- xhr.open('post', '{{$baseurl}}/wall_attach/{{$nickname}}', true);
+ // open editor if it isn't yet initialised
+ if (!editor) {
+ initEditor();
+ }
+ linkdrop(e);
- var data = new FormData();
- data.append('userfile', file);
- xhr.send(data);
}
</script>
diff --git a/view/tpl/jot.tpl b/view/tpl/jot.tpl
index dbbb470c5..cf99ede42 100755
--- a/view/tpl/jot.tpl
+++ b/view/tpl/jot.tpl
@@ -1,3 +1,4 @@
+<input id="invisible-wall-file-upload" type="file" name="files" style="visibility:hidden;position:absolute;top:-50;left:-50;width:0;height:0;" multiple>
<form id="profile-jot-form" action="{{$action}}" method="post" class="acl-form" data-form_id="profile-jot-form" data-allow_cid='{{$allow_cid}}' data-allow_gid='{{$allow_gid}}' data-deny_cid='{{$deny_cid}}' data-deny_gid='{{$deny_gid}}'>
{{$mimeselect}}
{{$layoutselect}}
@@ -39,7 +40,7 @@
</div>
{{/if}}
<div id="jot-text-wrap">
- <textarea class="profile-jot-text" id="profile-jot-text" name="body" tabindex="2" placeholder="{{$share}}" ondragenter="linkdropper(event);" ondragover="linkdropper(event);" ondrop="linkdrop(event);" >{{$content}}</textarea>
+ <textarea class="profile-jot-text" id="profile-jot-text" name="body" tabindex="2" placeholder="{{$share}}" >{{$content}}</textarea>
</div>
{{if $attachment}}
<div id="jot-attachment-wrap">