\
@@ -925,61 +926,67 @@ function initialize_create_image_dialog(dialog) {
var img_obj;
- // Upload is handled by FileUploader vendor plugin
- var uploader = new qq.FileUploaderBasic({
- button: $('#file-uploader',dialog)[0],
- action: 'upload',
- multiple: false,
- params: {},
- sizeLimit: 0,
- showMessage: function(message){
- //notifyMessage(message);
- },
- onSubmit: function(id, fileName){
- uploader.setParams({
- csrftoken: csrftoken,
- img : JSON.stringify(img_obj),
- file: fileName
- });
-
- $('#upload_progress_bars').append('
\
-
\
- '+tr("Uploading...")+'\
-
\
-
\
-
\
- \
-
\
-
'+fileName+'
\
-
\
-
');
- },
- onProgress: function(id, fileName, loaded, total){
- $('span.meter', $('div[id="'+fileName+'progressBar"]')).css('width', Math.floor(loaded*100/total)+'%')
- },
- onComplete: function(id, fileName, responseJSON){
-
- if (uploader._handler._xhrs[id] &&
- uploader._handler._xhrs[id].status == 500) {
-
- onError({}, JSON.parse(uploader._handler._xhrs[id].response) )
- $('div[id="'+fileName+'progressBar"]').remove();
- } else {
- notifyMessage("Image uploaded correctly");
- $('div[id="'+fileName+'progressBar"]').remove();
- Sunstone.runAction("Image.refresh");
- }
-
- return false;
- },
- onCancel: function(id, fileName){
+ var uploader = new Resumable({
+ target: '/upload_chunk',
+ chunkSize: 10*1024*1024,
+ maxFiles: 1,
+ testChunks: false,
+ query: {
+ csrftoken: csrftoken
}
});
+ uploader.assignBrowse($('#file-uploader',dialog)[0]);
+
+ var fileName = '';
var file_input = false;
- uploader._button._options.onChange = function(input) {
- file_input = input; return false;
- };
+
+ uploader.on('fileAdded', function(file){
+ fileName = file.fileName;
+ file_input = fileName;
+ });
+
+ uploader.on('uploadStart', function() {
+ $('#upload_progress_bars').append('
\
+
\
+ '+tr("Uploading...")+'\
+
\
+
\
+
\
+ \
+
\
+
'+fileName+'
\
+
\
+
');
+ });
+
+ uploader.on('progress', function() {
+ $('span.meter', $('div[id="'+fileName+'progressBar"]')).css('width', uploader.progress()*100.0+'%')
+ });
+
+ uploader.on('fileSuccess', function(file) {
+ $('div[id="'+fileName+'-info"]').text(tr('Registering in OpenNebula'));
+ $.ajax({
+ url: '/upload',
+ type: "POST",
+ data: {
+ csrftoken: csrftoken,
+ img : JSON.stringify(img_obj),
+ file: fileName,
+ tempfile: file.uniqueIdentifier
+ },
+ success: function(){
+ notifyMessage("Image uploaded correctly");
+ $('div[id="'+fileName+'progressBar"]').remove();
+ Sunstone.runAction("Image.refresh");
+ },
+ error: function(response){
+ //onError({}, JSON.parse(response) );
+ notifyMessage(response);
+ $('div[id="'+fileName+'progressBar"]').remove();
+ }
+ });
+ });
$('#create_image',dialog).submit(function(){
$create_image_dialog = dialog;
@@ -1063,7 +1070,8 @@ function initialize_create_image_dialog(dialog) {
dialog.empty();
setupCreateImageDialog();
- uploader._onInputChange(file_input);
+ //uploader._onInputChange(file_input);
+ uploader.upload();
} else {
Sunstone.runAction("Image.create", img_obj);
};
diff --git a/src/sunstone/public/vendor/resumable/MIT-LICENSE b/src/sunstone/public/vendor/resumable/MIT-LICENSE
new file mode 100644
index 0000000000..9d1da89a08
--- /dev/null
+++ b/src/sunstone/public/vendor/resumable/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2011, 23, http://www.23developer.com
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/src/sunstone/public/vendor/resumable/resumable.js b/src/sunstone/public/vendor/resumable/resumable.js
new file mode 100644
index 0000000000..43790307da
--- /dev/null
+++ b/src/sunstone/public/vendor/resumable/resumable.js
@@ -0,0 +1,817 @@
+/*
+* MIT Licensed
+* http://www.23developer.com/opensource
+* http://github.com/23/resumable.js
+* Steffen Tiedemann Christensen, steffen@23company.com
+*/
+
+(function(){
+"use strict";
+
+ var Resumable = function(opts){
+ if ( !(this instanceof Resumable) ) {
+ return new Resumable(opts);
+ }
+ this.version = 1.0;
+ // SUPPORTED BY BROWSER?
+ // Check if these features are support by the browser:
+ // - File object type
+ // - Blob object type
+ // - FileList object type
+ // - slicing files
+ this.support = (
+ (typeof(File)!=='undefined')
+ &&
+ (typeof(Blob)!=='undefined')
+ &&
+ (typeof(FileList)!=='undefined')
+ &&
+ (!!Blob.prototype.webkitSlice||!!Blob.prototype.mozSlice||!!Blob.prototype.slice||false)
+ );
+ if(!this.support) return(false);
+
+
+ // PROPERTIES
+ var $ = this;
+ $.files = [];
+ $.defaults = {
+ chunkSize:1*1024*1024,
+ forceChunkSize:false,
+ simultaneousUploads:3,
+ fileParameterName:'file',
+ throttleProgressCallbacks:0.5,
+ query:{},
+ headers:{},
+ preprocess:null,
+ method:'multipart',
+ prioritizeFirstAndLastChunk:false,
+ target:'/',
+ testChunks:true,
+ generateUniqueIdentifier:null,
+ maxChunkRetries:undefined,
+ chunkRetryInterval:undefined,
+ permanentErrors:[404, 415, 500, 501],
+ maxFiles:undefined,
+ withCredentials:false,
+ xhrTimeout:0,
+ maxFilesErrorCallback:function (files, errorCount) {
+ var maxFiles = $.getOpt('maxFiles');
+ alert('Please upload ' + maxFiles + ' file' + (maxFiles === 1 ? '' : 's') + ' at a time.');
+ },
+ minFileSize:1,
+ minFileSizeErrorCallback:function(file, errorCount) {
+ alert(file.fileName||file.name +' is too small, please upload files larger than ' + $h.formatSize($.getOpt('minFileSize')) + '.');
+ },
+ maxFileSize:undefined,
+ maxFileSizeErrorCallback:function(file, errorCount) {
+ alert(file.fileName||file.name +' is too large, please upload files less than ' + $h.formatSize($.getOpt('maxFileSize')) + '.');
+ },
+ fileType: [],
+ fileTypeErrorCallback: function(file, errorCount) {
+ alert(file.fileName||file.name +' has type not allowed, please upload files of type ' + $.getOpt('fileType') + '.');
+ }
+ };
+ $.opts = opts||{};
+ $.getOpt = function(o) {
+ var $opt = this;
+ // Get multiple option if passed an array
+ if(o instanceof Array) {
+ var options = {};
+ $h.each(o, function(option){
+ options[option] = $opt.getOpt(option);
+ });
+ return options;
+ }
+ // Otherwise, just return a simple option
+ if ($opt instanceof ResumableChunk) {
+ if (typeof $opt.opts[o] !== 'undefined') { return $opt.opts[o]; }
+ else { $opt = $opt.fileObj; }
+ }
+ if ($opt instanceof ResumableFile) {
+ if (typeof $opt.opts[o] !== 'undefined') { return $opt.opts[o]; }
+ else { $opt = $opt.resumableObj; }
+ }
+ if ($opt instanceof Resumable) {
+ if (typeof $opt.opts[o] !== 'undefined') { return $opt.opts[o]; }
+ else { return $opt.defaults[o]; }
+ }
+ };
+
+ // EVENTS
+ // catchAll(event, ...)
+ // fileSuccess(file), fileProgress(file), fileAdded(file, event), fileRetry(file), fileError(file, message),
+ // complete(), progress(), error(message, file), pause()
+ $.events = [];
+ $.on = function(event,callback){
+ $.events.push(event.toLowerCase(), callback);
+ };
+ $.fire = function(){
+ // `arguments` is an object, not array, in FF, so:
+ var args = [];
+ for (var i=0; i
0 && !$h.contains(o.fileType, fileType)) {
+ o.fileTypeErrorCallback(file, errorCount++);
+ return false;
+ }
+
+ if (typeof(o.minFileSize)!=='undefined' && file.sizeo.maxFileSize) {
+ o.maxFileSizeErrorCallback(file, errorCount++);
+ return false;
+ }
+
+ // directories have size == 0
+ if (!$.getFromUniqueIdentifier($h.generateUniqueIdentifier(file))) {(function(){
+ var f = new ResumableFile($, file);
+ window.setTimeout(function(){
+ $.files.push(f);
+ files.push(f);
+ f.container = (typeof event != 'undefined' ? event.srcElement : null);
+ $.fire('fileAdded', f, event)
+ },0);
+ })()};
+ });
+ window.setTimeout(function(){
+ $.fire('filesAdded', files)
+ },0);
+ };
+
+ // INTERNAL OBJECT TYPES
+ function ResumableFile(resumableObj, file){
+ var $ = this;
+ $.opts = {};
+ $.getOpt = resumableObj.getOpt;
+ $._prevProgress = 0;
+ $.resumableObj = resumableObj;
+ $.file = file;
+ $.fileName = file.fileName||file.name; // Some confusion in different versions of Firefox
+ $.size = file.size;
+ $.relativePath = file.webkitRelativePath || $.fileName;
+ $.uniqueIdentifier = $h.generateUniqueIdentifier(file);
+ $._pause = false;
+ $.container = '';
+ var _error = false;
+
+ // Callback when something happens within the chunk
+ var chunkEvent = function(event, message){
+ // event can be 'progress', 'success', 'error' or 'retry'
+ switch(event){
+ case 'progress':
+ $.resumableObj.fire('fileProgress', $);
+ break;
+ case 'error':
+ $.abort();
+ _error = true;
+ $.chunks = [];
+ $.resumableObj.fire('fileError', $, message);
+ break;
+ case 'success':
+ if(_error) return;
+ $.resumableObj.fire('fileProgress', $); // it's at least progress
+ if($.isComplete()) {
+ $.resumableObj.fire('fileSuccess', $, message);
+ }
+ break;
+ case 'retry':
+ $.resumableObj.fire('fileRetry', $);
+ break;
+ }
+ };
+
+ // Main code to set up a file object with chunks,
+ // packaged to be able to handle retries if needed.
+ $.chunks = [];
+ $.abort = function(){
+ // Stop current uploads
+ var abortCount = 0;
+ $h.each($.chunks, function(c){
+ if(c.status()=='uploading') {
+ c.abort();
+ abortCount++;
+ }
+ });
+ if(abortCount>0) $.resumableObj.fire('fileProgress', $);
+ };
+ $.cancel = function(){
+ // Reset this file to be void
+ var _chunks = $.chunks;
+ $.chunks = [];
+ // Stop current uploads
+ $h.each(_chunks, function(c){
+ if(c.status()=='uploading') {
+ c.abort();
+ $.resumableObj.uploadNextChunk();
+ }
+ });
+ $.resumableObj.removeFile($);
+ $.resumableObj.fire('fileProgress', $);
+ };
+ $.retry = function(){
+ $.bootstrap();
+ var firedRetry = false;
+ $.resumableObj.on('chunkingComplete', function(){
+ if(!firedRetry) $.resumableObj.upload();
+ firedRetry = true;
+ });
+ };
+ $.bootstrap = function(){
+ $.abort();
+ _error = false;
+ // Rebuild stack of chunks from file
+ $.chunks = [];
+ $._prevProgress = 0;
+ var round = $.getOpt('forceChunkSize') ? Math.ceil : Math.floor;
+ var maxOffset = Math.max(round($.file.size/$.getOpt('chunkSize')),1);
+ for (var offset=0; offset0.999 ? 1 : ret));
+ ret = Math.max($._prevProgress, ret); // We don't want to lose percentages when an upload is paused
+ $._prevProgress = ret;
+ return(ret);
+ };
+ $.isUploading = function(){
+ var uploading = false;
+ $h.each($.chunks, function(chunk){
+ if(chunk.status()=='uploading') {
+ uploading = true;
+ return(false);
+ }
+ });
+ return(uploading);
+ };
+ $.isComplete = function(){
+ var outstanding = false;
+ $h.each($.chunks, function(chunk){
+ var status = chunk.status();
+ if(status=='pending' || status=='uploading' || chunk.preprocessState === 1) {
+ outstanding = true;
+ return(false);
+ }
+ });
+ return(!outstanding);
+ };
+ $.pause = function(pause){
+ if(typeof(pause)==='undefined'){
+ $._pause = ($._pause ? false : true);
+ }else{
+ $._pause = pause;
+ }
+ };
+ $.isPaused = function() {
+ return $._pause;
+ };
+
+
+ // Bootstrap and return
+ $.resumableObj.fire('chunkingStart', $);
+ $.bootstrap();
+ return(this);
+ }
+
+ function ResumableChunk(resumableObj, fileObj, offset, callback){
+ var $ = this;
+ $.opts = {};
+ $.getOpt = resumableObj.getOpt;
+ $.resumableObj = resumableObj;
+ $.fileObj = fileObj;
+ $.fileObjSize = fileObj.size;
+ $.fileObjType = fileObj.file.type;
+ $.offset = offset;
+ $.callback = callback;
+ $.lastProgressCallback = (new Date);
+ $.tested = false;
+ $.retries = 0;
+ $.pendingRetry = false;
+ $.preprocessState = 0; // 0 = unprocessed, 1 = processing, 2 = finished
+
+ // Computed properties
+ var chunkSize = $.getOpt('chunkSize');
+ $.loaded = 0;
+ $.startByte = $.offset*chunkSize;
+ $.endByte = Math.min($.fileObjSize, ($.offset+1)*chunkSize);
+ if ($.fileObjSize-$.endByte < chunkSize && !$.getOpt('forceChunkSize')) {
+ // The last chunk will be bigger than the chunk size, but less than 2*chunkSize
+ $.endByte = $.fileObjSize;
+ }
+ $.xhr = null;
+
+ // test() makes a GET request without any data to see if the chunk has already been uploaded in a previous session
+ $.test = function(){
+ // Set up request and listen for event
+ $.xhr = new XMLHttpRequest();
+
+ var testHandler = function(e){
+ $.tested = true;
+ var status = $.status();
+ if(status=='success') {
+ $.callback(status, $.message());
+ $.resumableObj.uploadNextChunk();
+ } else {
+ $.send();
+ }
+ };
+ $.xhr.addEventListener('load', testHandler, false);
+ $.xhr.addEventListener('error', testHandler, false);
+
+ // Add data from the query options
+ var params = [];
+ var customQuery = $.getOpt('query');
+ if(typeof customQuery == 'function') customQuery = customQuery($.fileObj, $);
+ $h.each(customQuery, function(k,v){
+ params.push([encodeURIComponent(k), encodeURIComponent(v)].join('='));
+ });
+ // Add extra data to identify chunk
+ params.push(['resumableChunkNumber', encodeURIComponent($.offset+1)].join('='));
+ params.push(['resumableChunkSize', encodeURIComponent($.getOpt('chunkSize'))].join('='));
+ params.push(['resumableCurrentChunkSize', encodeURIComponent($.endByte - $.startByte)].join('='));
+ params.push(['resumableTotalSize', encodeURIComponent($.fileObjSize)].join('='));
+ params.push(['resumableType', encodeURIComponent($.fileObjType)].join('='));
+ params.push(['resumableIdentifier', encodeURIComponent($.fileObj.uniqueIdentifier)].join('='));
+ params.push(['resumableFilename', encodeURIComponent($.fileObj.fileName)].join('='));
+ params.push(['resumableRelativePath', encodeURIComponent($.fileObj.relativePath)].join('='));
+ params.push(['resumableTotalChunks', encodeURIComponent($.fileObj.chunks.length)].join('='));
+ // Append the relevant chunk and send it
+ $.xhr.open('GET', $h.getTarget(params));
+ $.xhr.timeout = $.getOpt('xhrTimeout');
+ $.xhr.withCredentials = $.getOpt('withCredentials');
+ // Add data from header options
+ $h.each($.getOpt('headers'), function(k,v) {
+ $.xhr.setRequestHeader(k, v);
+ });
+ $.xhr.send(null);
+ };
+
+ $.preprocessFinished = function(){
+ $.preprocessState = 2;
+ $.send();
+ };
+
+ // send() uploads the actual data in a POST call
+ $.send = function(){
+ var preprocess = $.getOpt('preprocess');
+ if(typeof preprocess === 'function') {
+ switch($.preprocessState) {
+ case 0: preprocess($); $.preprocessState = 1; return;
+ case 1: return;
+ case 2: break;
+ }
+ }
+ if($.getOpt('testChunks') && !$.tested) {
+ $.test();
+ return;
+ }
+
+ // Set up request and listen for event
+ $.xhr = new XMLHttpRequest();
+
+ // Progress
+ $.xhr.upload.addEventListener('progress', function(e){
+ if( (new Date) - $.lastProgressCallback > $.getOpt('throttleProgressCallbacks') * 1000 ) {
+ $.callback('progress');
+ $.lastProgressCallback = (new Date);
+ }
+ $.loaded=e.loaded||0;
+ }, false);
+ $.loaded = 0;
+ $.pendingRetry = false;
+ $.callback('progress');
+
+ // Done (either done, failed or retry)
+ var doneHandler = function(e){
+ var status = $.status();
+ if(status=='success'||status=='error') {
+ $.callback(status, $.message());
+ $.resumableObj.uploadNextChunk();
+ } else {
+ $.callback('retry', $.message());
+ $.abort();
+ $.retries++;
+ var retryInterval = $.getOpt('chunkRetryInterval');
+ if(retryInterval !== undefined) {
+ $.pendingRetry = true;
+ setTimeout($.send, retryInterval);
+ } else {
+ $.send();
+ }
+ }
+ };
+ $.xhr.addEventListener('load', doneHandler, false);
+ $.xhr.addEventListener('error', doneHandler, false);
+
+ // Set up the basic query data from Resumable
+ var query = {
+ resumableChunkNumber: $.offset+1,
+ resumableChunkSize: $.getOpt('chunkSize'),
+ resumableCurrentChunkSize: $.endByte - $.startByte,
+ resumableTotalSize: $.fileObjSize,
+ resumableType: $.fileObjType,
+ resumableIdentifier: $.fileObj.uniqueIdentifier,
+ resumableFilename: $.fileObj.fileName,
+ resumableRelativePath: $.fileObj.relativePath,
+ resumableTotalChunks: $.fileObj.chunks.length
+ };
+ // Mix in custom data
+ var customQuery = $.getOpt('query');
+ if(typeof customQuery == 'function') customQuery = customQuery($.fileObj, $);
+ $h.each(customQuery, function(k,v){
+ query[k] = v;
+ });
+
+ var func = ($.fileObj.file.slice ? 'slice' : ($.fileObj.file.mozSlice ? 'mozSlice' : ($.fileObj.file.webkitSlice ? 'webkitSlice' : 'slice'))),
+ bytes = $.fileObj.file[func]($.startByte,$.endByte),
+ data = null,
+ target = $.getOpt('target');
+
+ if ($.getOpt('method') === 'octet') {
+ // Add data from the query options
+ data = bytes;
+ var params = [];
+ $h.each(query, function(k,v){
+ params.push([encodeURIComponent(k), encodeURIComponent(v)].join('='));
+ });
+ target = $h.getTarget(params);
+ } else {
+ // Add data from the query options
+ data = new FormData();
+ $h.each(query, function(k,v){
+ data.append(k,v);
+ });
+ data.append($.getOpt('fileParameterName'), bytes);
+ }
+
+ $.xhr.open('POST', target);
+ $.xhr.timeout = $.getOpt('xhrTimeout');
+ $.xhr.withCredentials = $.getOpt('withCredentials');
+ // Add data from header options
+ $h.each($.getOpt('headers'), function(k,v) {
+ $.xhr.setRequestHeader(k, v);
+ });
+ $.xhr.send(data);
+ };
+ $.abort = function(){
+ // Abort and reset
+ if($.xhr) $.xhr.abort();
+ $.xhr = null;
+ };
+ $.status = function(){
+ // Returns: 'pending', 'uploading', 'success', 'error'
+ if($.pendingRetry) {
+ // if pending retry then that's effectively the same as actively uploading,
+ // there might just be a slight delay before the retry starts
+ return('uploading');
+ } else if(!$.xhr) {
+ return('pending');
+ } else if($.xhr.readyState<4) {
+ // Status is really 'OPENED', 'HEADERS_RECEIVED' or 'LOADING' - meaning that stuff is happening
+ return('uploading');
+ } else {
+ if($.xhr.status==200) {
+ // HTTP 200, perfect
+ return('success');
+ } else if($h.contains($.getOpt('permanentErrors'), $.xhr.status) || $.retries >= $.getOpt('maxChunkRetries')) {
+ // HTTP 415/500/501, permanent error
+ return('error');
+ } else {
+ // this should never happen, but we'll reset and queue a retry
+ // a likely case for this would be 503 service unavailable
+ $.abort();
+ return('pending');
+ }
+ }
+ };
+ $.message = function(){
+ return($.xhr ? $.xhr.responseText : '');
+ };
+ $.progress = function(relative){
+ if(typeof(relative)==='undefined') relative = false;
+ var factor = (relative ? ($.endByte-$.startByte)/$.fileObjSize : 1);
+ if($.pendingRetry) return(0);
+ var s = $.status();
+ switch(s){
+ case 'success':
+ case 'error':
+ return(1*factor);
+ case 'pending':
+ return(0*factor);
+ default:
+ return($.loaded/($.endByte-$.startByte)*factor);
+ }
+ };
+ return(this);
+ }
+
+ // QUEUE
+ $.uploadNextChunk = function(){
+ var found = false;
+
+ // In some cases (such as videos) it's really handy to upload the first
+ // and last chunk of a file quickly; this let's the server check the file's
+ // metadata and determine if there's even a point in continuing.
+ if ($.getOpt('prioritizeFirstAndLastChunk')) {
+ $h.each($.files, function(file){
+ if(file.chunks.length && file.chunks[0].status()=='pending' && file.chunks[0].preprocessState === 0) {
+ file.chunks[0].send();
+ found = true;
+ return(false);
+ }
+ if(file.chunks.length>1 && file.chunks[file.chunks.length-1].status()=='pending' && file.chunks[file.chunks.length-1].preprocessState === 0) {
+ file.chunks[file.chunks.length-1].send();
+ found = true;
+ return(false);
+ }
+ });
+ if(found) return(true);
+ }
+
+ // Now, simply look for the next, best thing to upload
+ $h.each($.files, function(file){
+ if(file.isPaused()===false){
+ $h.each(file.chunks, function(chunk){
+ if(chunk.status()=='pending' && chunk.preprocessState === 0) {
+ chunk.send();
+ found = true;
+ return(false);
+ }
+ });
+ }
+ if(found) return(false);
+ });
+ if(found) return(true);
+
+ // The are no more outstanding chunks to upload, check is everything is done
+ var outstanding = false;
+ $h.each($.files, function(file){
+ if(!file.isComplete()) {
+ outstanding = true;
+ return(false);
+ }
+ });
+ if(!outstanding) {
+ // All chunks have been uploaded, complete
+ $.fire('complete');
+ }
+ return(false);
+ };
+
+
+ // PUBLIC METHODS FOR RESUMABLE.JS
+ $.assignBrowse = function(domNodes, isDirectory){
+ if(typeof(domNodes.length)=='undefined') domNodes = [domNodes];
+
+ $h.each(domNodes, function(domNode) {
+ var input;
+ if(domNode.tagName==='INPUT' && domNode.type==='file'){
+ input = domNode;
+ } else {
+ input = document.createElement('input');
+ input.setAttribute('type', 'file');
+ input.style.display = 'none';
+ domNode.addEventListener('click', function(){
+ input.style.opacity = 0;
+ input.style.display='block';
+ input.focus();
+ input.click();
+ input.style.display='none';
+ }, false);
+ domNode.appendChild(input);
+ }
+ var maxFiles = $.getOpt('maxFiles');
+ if (typeof(maxFiles)==='undefined'||maxFiles!=1){
+ input.setAttribute('multiple', 'multiple');
+ } else {
+ input.removeAttribute('multiple');
+ }
+ if(isDirectory){
+ input.setAttribute('webkitdirectory', 'webkitdirectory');
+ } else {
+ input.removeAttribute('webkitdirectory');
+ }
+ // When new files are added, simply append them to the overall list
+ input.addEventListener('change', function(e){
+ appendFilesFromFileList(e.target.files,e);
+ e.target.value = '';
+ }, false);
+ });
+ };
+ $.assignDrop = function(domNodes){
+ if(typeof(domNodes.length)=='undefined') domNodes = [domNodes];
+
+ $h.each(domNodes, function(domNode) {
+ domNode.addEventListener('dragover', onDragOver, false);
+ domNode.addEventListener('drop', onDrop, false);
+ });
+ };
+ $.unAssignDrop = function(domNodes) {
+ if (typeof(domNodes.length) == 'undefined') domNodes = [domNodes];
+
+ $h.each(domNodes, function(domNode) {
+ domNode.removeEventListener('dragover', onDragOver);
+ domNode.removeEventListener('drop', onDrop);
+ });
+ };
+ $.isUploading = function(){
+ var uploading = false;
+ $h.each($.files, function(file){
+ if (file.isUploading()) {
+ uploading = true;
+ return(false);
+ }
+ });
+ return(uploading);
+ };
+ $.upload = function(){
+ // Make sure we don't start too many uploads at once
+ if($.isUploading()) return;
+ // Kick off the queue
+ $.fire('uploadStart');
+ for (var num=1; num<=$.getOpt('simultaneousUploads'); num++) {
+ $.uploadNextChunk();
+ }
+ };
+ $.pause = function(){
+ // Resume all chunks currently being uploaded
+ $h.each($.files, function(file){
+ file.abort();
+ });
+ $.fire('pause');
+ };
+ $.cancel = function(){
+ for(var i = $.files.length - 1; i >= 0; i--) {
+ $.files[i].cancel();
+ }
+ $.fire('cancel');
+ };
+ $.progress = function(){
+ var totalDone = 0;
+ var totalSize = 0;
+ // Resume all chunks currently being uploaded
+ $h.each($.files, function(file){
+ totalDone += file.progress()*file.size;
+ totalSize += file.size;
+ });
+ return(totalSize>0 ? totalDone/totalSize : 0);
+ };
+ $.addFile = function(file, event){
+ appendFilesFromFileList([file], event);
+ };
+ $.removeFile = function(file){
+ for(var i = $.files.length - 1; i >= 0; i--) {
+ if($.files[i] === file) {
+ $.files.splice(i, 1);
+ }
+ }
+ };
+ $.getFromUniqueIdentifier = function(uniqueIdentifier){
+ var ret = false;
+ $h.each($.files, function(f){
+ if(f.uniqueIdentifier==uniqueIdentifier) ret = f;
+ });
+ return(ret);
+ };
+ $.getSize = function(){
+ var totalSize = 0;
+ $h.each($.files, function(file){
+ totalSize += file.size;
+ });
+ return(totalSize);
+ };
+
+ return(this);
+ };
+
+
+ // Node.js-style export for Node and Component
+ if (typeof module != 'undefined') {
+ module.exports = Resumable;
+ } else if (typeof define === "function" && define.amd) {
+ // AMD/requirejs: Define the module
+ define(function(){
+ return Resumable;
+ });
+ } else {
+ // Browser: Expose to window
+ window.Resumable = Resumable;
+ }
+
+})();
diff --git a/src/sunstone/sunstone-server.rb b/src/sunstone/sunstone-server.rb
index b396523fbc..25608cb71a 100755
--- a/src/sunstone/sunstone-server.rb
+++ b/src/sunstone/sunstone-server.rb
@@ -58,6 +58,8 @@ require 'sinatra'
require 'erb'
require 'yaml'
require 'securerandom'
+require 'tmpdir'
+require 'fileutils'
require 'CloudAuth'
require 'SunstoneServer'
@@ -500,27 +502,63 @@ end
##############################################################################
post '/upload' do
tmpfile = nil
- rackinput = request.env['rack.input']
- if (rackinput.class == Tempfile)
- tmpfile = rackinput
- elsif rackinput.respond_to?('read')
- tmpfile = Tempfile.open('sunstone-upload')
- tmpfile.write(rackinput.read)
- tmpfile.flush
- else
- logger.error { "Unexpected rackinput class #{rackinput.class}" }
- [500, ""]
- end
+ name = params[:tempfile]
- if tmpfile.size == 0
+ if !name
[500, OpenNebula::Error.new("There was a problem uploading the file, " \
"please check the permissions on the file").to_json]
else
- @SunstoneServer.upload(params[:img], tmpfile.path)
+ tmpfile = File.join(Dir.tmpdir, name)
+ res = @SunstoneServer.upload(params[:img], tmpfile)
+ FileUtils.rm(tmpfile)
+ res
end
end
+post '/upload_chunk' do
+ info = env['rack.request.form_hash']
+ chunk_number = info['resumableChunkNumber'].to_i - 1
+ chunk_size = info['resumableChunkSize'].to_i
+ chunk_current_size = info['resumableCurrentChunkSize'].to_i
+ chunk_start = chunk_number * chunk_size
+ chunk_end = chunk_start + chunk_current_size - 1
+ identifier = info['']
+ size = info['resumableTotalSize'].to_i
+
+ file_name = info['resumableIdentifier']
+ file_path = File.join(Dir.tmpdir, file_name)
+
+ tmpfile=env['rack.request.form_hash']['file'][:tempfile]
+
+ begin
+ chunk = tmpfile.read
+ rescue => e
+ STDERR.puts e.backtrace
+ return [500, OpenNebula::Error.new("Could not read the uploaded " \
+ "chunk.".to_json)]
+ end
+
+ if File.exist? file_name
+ mode = "r+"
+ else
+ mode = "w"
+ end
+
+ begin
+ open(file_path, mode) do |f|
+ f.seek(chunk_start)
+ f.write_nonblock(chunk)
+ end
+ rescue => e
+ STDERR.puts e.backtrace
+ return [500, OpenNebula::Error.new("Can not write to the temporary" \
+ " image file").to_json]
+ end
+
+ ""
+end
+
##############################################################################
# Create a new Resource
##############################################################################
diff --git a/src/sunstone/views/index.erb b/src/sunstone/views/index.erb
index 7434466331..8d4cb966f3 100644
--- a/src/sunstone/views/index.erb
+++ b/src/sunstone/views/index.erb
@@ -35,6 +35,7 @@
+