2015-06-23 10:16:53 +02:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
import React from 'react/addons';
|
|
|
|
|
|
|
|
import promise from 'es6-promise';
|
|
|
|
promise.polyfill();
|
|
|
|
|
|
|
|
import fetch from 'isomorphic-fetch';
|
2015-06-29 10:00:26 +02:00
|
|
|
import AppConstants from '../../constants/application_constants';
|
|
|
|
|
2015-06-29 11:01:45 +02:00
|
|
|
import { getCookie } from '../../utils/fetch_api_utils';
|
2015-06-23 10:16:53 +02:00
|
|
|
|
|
|
|
import fineUploader from 'fineUploader';
|
|
|
|
import FileDragAndDrop from './file_drag_and_drop';
|
2015-06-29 16:00:26 +02:00
|
|
|
import FileDragAndDropToolbar from './file_drag_and_drop_toolbar';
|
2015-06-23 10:16:53 +02:00
|
|
|
|
2015-06-25 11:12:40 +02:00
|
|
|
import GlobalNotificationModel from '../../models/global_notification_model';
|
|
|
|
import GlobalNotificationActions from '../../actions/global_notification_actions';
|
|
|
|
|
2015-06-23 10:16:53 +02:00
|
|
|
var ReactS3FineUploader = React.createClass({
|
|
|
|
|
|
|
|
propTypes: {
|
|
|
|
keyRoutine: React.PropTypes.shape({
|
|
|
|
url: React.PropTypes.string,
|
2015-06-29 10:00:26 +02:00
|
|
|
fileClass: React.PropTypes.string,
|
|
|
|
bitcoinId: React.PropTypes.string
|
2015-06-23 10:16:53 +02:00
|
|
|
}),
|
2015-06-23 13:55:05 +02:00
|
|
|
createBlobRoutine: React.PropTypes.shape({
|
|
|
|
url: React.PropTypes.string
|
|
|
|
}),
|
2015-06-29 11:44:16 +02:00
|
|
|
submitKey: React.PropTypes.func,
|
2015-06-23 10:16:53 +02:00
|
|
|
autoUpload: React.PropTypes.bool,
|
|
|
|
debug: React.PropTypes.bool,
|
|
|
|
objectProperties: React.PropTypes.shape({
|
|
|
|
acl: React.PropTypes.string
|
|
|
|
}),
|
|
|
|
request: React.PropTypes.shape({
|
|
|
|
endpoint: React.PropTypes.string,
|
|
|
|
accessKey: React.PropTypes.string,
|
|
|
|
params: React.PropTypes.shape({
|
|
|
|
csrfmiddlewaretoken: React.PropTypes.string
|
|
|
|
})
|
|
|
|
}),
|
|
|
|
signature: React.PropTypes.shape({
|
|
|
|
endpoint: React.PropTypes.string
|
|
|
|
}),
|
|
|
|
uploadSuccess: React.PropTypes.shape({
|
|
|
|
method: React.PropTypes.string,
|
|
|
|
endpoint: React.PropTypes.string,
|
|
|
|
params: React.PropTypes.shape({
|
|
|
|
isBrowserPreviewCapable: React.PropTypes.any, // maybe fix this later
|
|
|
|
bitcoin_ID_noPrefix: React.PropTypes.string
|
|
|
|
})
|
|
|
|
}),
|
|
|
|
cors: React.PropTypes.shape({
|
|
|
|
expected: React.PropTypes.bool
|
|
|
|
}),
|
|
|
|
chunking: React.PropTypes.shape({
|
|
|
|
enabled: React.PropTypes.bool
|
|
|
|
}),
|
|
|
|
resume: React.PropTypes.shape({
|
|
|
|
enabled: React.PropTypes.bool
|
|
|
|
}),
|
|
|
|
deleteFile: React.PropTypes.shape({
|
|
|
|
enabled: React.PropTypes.bool,
|
|
|
|
method: React.PropTypes.string,
|
2015-06-29 10:01:54 +02:00
|
|
|
endpoint: React.PropTypes.string,
|
|
|
|
customHeaders: React.PropTypes.object
|
2015-06-23 10:16:53 +02:00
|
|
|
}),
|
|
|
|
session: React.PropTypes.shape({
|
|
|
|
endpoint: React.PropTypes.bool
|
|
|
|
}),
|
|
|
|
validation: React.PropTypes.shape({
|
|
|
|
itemLimit: React.PropTypes.number,
|
|
|
|
sizeLimit: React.PropTypes.string
|
|
|
|
}),
|
|
|
|
messages: React.PropTypes.shape({
|
|
|
|
unsupportedBrowser: React.PropTypes.string
|
|
|
|
}),
|
|
|
|
formatFileName: React.PropTypes.func,
|
|
|
|
multiple: React.PropTypes.bool,
|
|
|
|
retry: React.PropTypes.shape({
|
|
|
|
enableAuto: React.PropTypes.bool
|
2015-06-29 11:44:16 +02:00
|
|
|
}),
|
|
|
|
setUploadStatus: React.PropTypes.func,
|
|
|
|
isReadyForFormSubmission: React.PropTypes.func
|
2015-06-23 10:16:53 +02:00
|
|
|
},
|
|
|
|
|
2015-06-29 10:00:26 +02:00
|
|
|
getDefaultProps() {
|
|
|
|
return {
|
|
|
|
autoUpload: true,
|
|
|
|
debug: false,
|
|
|
|
objectProperties: {
|
|
|
|
acl: 'public-read',
|
|
|
|
bucket: 'ascribe0'
|
|
|
|
},
|
|
|
|
request: {
|
|
|
|
endpoint: 'https://ascribe0.s3.amazonaws.com',
|
|
|
|
accessKey: 'AKIAIVCZJ33WSCBQ3QDA'
|
|
|
|
},
|
|
|
|
uploadSuccess: {
|
|
|
|
params: {
|
|
|
|
isBrowserPreviewCapable: fineUploader.supportedFeatures.imagePreviews
|
|
|
|
}
|
|
|
|
},
|
|
|
|
signature: {
|
2015-06-29 11:01:45 +02:00
|
|
|
endpoint: AppConstants.serverUrl + 's3/signature/',
|
|
|
|
customHeaders: {
|
|
|
|
'X-CSRFToken': getCookie('csrftoken')
|
2015-06-29 11:44:16 +02:00
|
|
|
}
|
2015-06-29 10:00:26 +02:00
|
|
|
},
|
|
|
|
deleteFile: {
|
|
|
|
enabled: true,
|
|
|
|
method: 'DELETE',
|
2015-06-29 11:01:45 +02:00
|
|
|
endpoint: AppConstants.serverUrl + 's3/delete',
|
|
|
|
customHeaders: {
|
|
|
|
'X-CSRFToken': getCookie('csrftoken')
|
|
|
|
}
|
2015-06-29 10:00:26 +02:00
|
|
|
},
|
|
|
|
cors: {
|
2015-06-29 11:01:45 +02:00
|
|
|
expected: true,
|
|
|
|
sendCredentials: true
|
2015-06-29 10:00:26 +02:00
|
|
|
},
|
|
|
|
chunking: {
|
|
|
|
enabled: true
|
|
|
|
},
|
|
|
|
resume: {
|
|
|
|
enabled: true
|
|
|
|
},
|
|
|
|
retry: {
|
|
|
|
enableAuto: false
|
|
|
|
},
|
|
|
|
session: {
|
|
|
|
endpoint: null
|
|
|
|
},
|
|
|
|
messages: {
|
|
|
|
unsupportedBrowser: '<h3>Upload is not functional in IE7 as IE7 has no support for CORS!</h3>'
|
|
|
|
},
|
|
|
|
formatFileName: function(name){// fix maybe
|
|
|
|
if (name !== undefined && name.length > 26) {
|
|
|
|
name = name.slice(0, 15) + '...' + name.slice(-15);
|
|
|
|
}
|
|
|
|
return name;
|
|
|
|
},
|
2015-06-29 16:00:26 +02:00
|
|
|
multiple: false
|
2015-06-29 10:00:26 +02:00
|
|
|
};
|
|
|
|
},
|
2015-06-29 11:01:45 +02:00
|
|
|
|
2015-06-23 13:55:05 +02:00
|
|
|
getInitialState() {
|
|
|
|
return {
|
|
|
|
filesToUpload: [],
|
|
|
|
uploader: new fineUploader.s3.FineUploaderBasic(this.propsToConfig())
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2015-06-23 10:16:53 +02:00
|
|
|
propsToConfig() {
|
|
|
|
let objectProperties = this.props.objectProperties;
|
2015-06-23 13:55:05 +02:00
|
|
|
objectProperties.key = this.requestKey;
|
2015-06-23 10:16:53 +02:00
|
|
|
|
|
|
|
return {
|
|
|
|
autoUpload: this.props.autoUpload,
|
|
|
|
debug: this.props.debug,
|
|
|
|
objectProperties: objectProperties, // do a special key handling here
|
|
|
|
request: this.props.request,
|
|
|
|
signature: this.props.signature,
|
|
|
|
uploadSuccess: this.props.uploadSuccess,
|
|
|
|
cors: this.props.cors,
|
|
|
|
chunking: this.props.chunking,
|
|
|
|
resume: this.props.resume,
|
|
|
|
deleteFile: this.props.deleteFile,
|
|
|
|
session: this.props.session,
|
|
|
|
validation: this.props.validation,
|
|
|
|
messages: this.props.messages,
|
|
|
|
formatFileName: this.props.formatFileName,
|
|
|
|
multiple: this.props.multiple,
|
|
|
|
retry: this.props.retry,
|
|
|
|
callbacks: {
|
|
|
|
onSubmit: this.onSubmit,
|
|
|
|
onComplete: this.onComplete,
|
2015-06-29 10:01:54 +02:00
|
|
|
onCancel: this.onCancel,
|
2015-06-23 10:16:53 +02:00
|
|
|
onDelete: this.onDelete,
|
2015-06-23 13:55:05 +02:00
|
|
|
onSessionRequestComplete: this.onSessionRequestComplete,
|
2015-06-23 10:16:53 +02:00
|
|
|
onProgress: this.onProgress,
|
|
|
|
onRetry: this.onRetry,
|
|
|
|
onAutoRetry: this.onAutoRetry,
|
|
|
|
onManualRetry: this.onManualRetry,
|
|
|
|
onDeleteComplete: this.onDeleteComplete
|
|
|
|
}
|
|
|
|
};
|
|
|
|
},
|
2015-06-23 13:55:05 +02:00
|
|
|
|
2015-06-23 10:16:53 +02:00
|
|
|
getCookie(name) {
|
|
|
|
let value = '; ' + document.cookie;
|
|
|
|
let parts = value.split('; ' + name + '=');
|
|
|
|
if (parts.length === 2) {
|
|
|
|
return parts.pop().split(';').shift();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
requestKey(fileId) {
|
|
|
|
let filename = this.state.uploader.getName(fileId);
|
2015-06-24 15:52:43 +02:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
fetch(this.props.keyRoutine.url, {
|
|
|
|
method: 'post',
|
|
|
|
headers: {
|
|
|
|
'Accept': 'application/json',
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
'X-CSRFToken': this.getCookie('csrftoken')
|
|
|
|
},
|
|
|
|
credentials: 'include',
|
|
|
|
body: JSON.stringify({
|
|
|
|
'filename': filename,
|
2015-06-29 10:00:26 +02:00
|
|
|
'file_class': this.props.keyRoutine.fileClass,
|
|
|
|
'bitcoin_id': this.props.keyRoutine.bitcoinId
|
2015-06-24 15:52:43 +02:00
|
|
|
})
|
2015-06-23 10:16:53 +02:00
|
|
|
})
|
2015-06-24 15:52:43 +02:00
|
|
|
.then((res) => {
|
|
|
|
return res.json();
|
|
|
|
})
|
|
|
|
.then((res) =>{
|
|
|
|
resolve(res.key);
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
console.error(err);
|
|
|
|
reject(err);
|
|
|
|
});
|
2015-06-23 10:16:53 +02:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
/* FineUploader specific callback function handlers */
|
|
|
|
|
|
|
|
onSubmit() {
|
|
|
|
console.log('submit');
|
|
|
|
},
|
|
|
|
|
2015-06-23 13:55:05 +02:00
|
|
|
onComplete(id) {
|
|
|
|
let files = this.state.filesToUpload;
|
|
|
|
files[id].status = 'upload successful';
|
|
|
|
files[id].key = this.state.uploader.getKey(id);
|
|
|
|
|
|
|
|
let newState = React.addons.update(this.state, {
|
|
|
|
filesToUpload: { $set: files }
|
|
|
|
});
|
|
|
|
this.setState(newState);
|
|
|
|
this.createBlob(files[id]);
|
2015-06-29 11:44:16 +02:00
|
|
|
this.props.submitKey(files[id].key);
|
|
|
|
|
|
|
|
// also, lets check if after the completion of this upload,
|
|
|
|
// the form is ready for submission or not
|
|
|
|
if(this.props.isReadyForFormSubmission && this.props.isReadyForFormSubmission(this.state.filesToUpload)) {
|
|
|
|
// if so, set uploadstatus to true
|
|
|
|
this.props.setUploadStatus(true);
|
|
|
|
} else {
|
|
|
|
this.props.setUploadStatus(false);
|
|
|
|
}
|
2015-06-23 13:55:05 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
createBlob(file) {
|
|
|
|
let defer = new fineUploader.Promise();
|
|
|
|
fetch(this.props.createBlobRoutine.url, {
|
|
|
|
method: 'post',
|
|
|
|
headers: {
|
|
|
|
'Accept': 'application/json',
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
'X-CSRFToken': this.getCookie('csrftoken')
|
|
|
|
},
|
|
|
|
credentials: 'include',
|
|
|
|
body: JSON.stringify({
|
|
|
|
'filename': file.name,
|
|
|
|
'key': file.key
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.then((res) => {
|
|
|
|
return res.json();
|
|
|
|
})
|
|
|
|
.then((res) =>{
|
|
|
|
defer.success(res.key);
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
console.error(err);
|
|
|
|
});
|
|
|
|
return defer;
|
2015-06-23 10:16:53 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
onRetry() {
|
|
|
|
console.log('retry');
|
|
|
|
},
|
|
|
|
|
|
|
|
onAutoRetry() {
|
|
|
|
console.log('autoretry');
|
|
|
|
},
|
|
|
|
|
|
|
|
onManualRetry() {
|
|
|
|
console.log('manualretry');
|
|
|
|
},
|
|
|
|
|
|
|
|
onDelete() {
|
|
|
|
console.log('delete');
|
|
|
|
},
|
|
|
|
|
2015-06-29 10:01:54 +02:00
|
|
|
onCancel(id) {
|
|
|
|
this.removeFileWithIdFromFilesToUpload(id);
|
|
|
|
|
2015-06-29 16:00:26 +02:00
|
|
|
let notification = new GlobalNotificationModel('File upload canceled', 'success', 5000);
|
2015-06-29 10:01:54 +02:00
|
|
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
2015-06-29 11:44:16 +02:00
|
|
|
|
|
|
|
if(this.props.isReadyForFormSubmission && this.props.isReadyForFormSubmission(this.state.filesToUpload)) {
|
|
|
|
// if so, set uploadstatus to true
|
|
|
|
this.props.setUploadStatus(true);
|
|
|
|
} else {
|
|
|
|
this.props.setUploadStatus(false);
|
|
|
|
}
|
2015-06-23 10:16:53 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
onDeleteComplete(id, xhr, isError) {
|
|
|
|
if(isError) {
|
2015-06-25 14:15:02 +02:00
|
|
|
let notification = new GlobalNotificationModel('Couldn\'t delete file', 'danger', 10000);
|
|
|
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
|
|
|
} else {
|
2015-06-29 10:01:54 +02:00
|
|
|
this.removeFileWithIdFromFilesToUpload(id);
|
2015-06-25 14:15:02 +02:00
|
|
|
|
2015-06-29 16:00:26 +02:00
|
|
|
let notification = new GlobalNotificationModel('File deleted', 'success', 5000);
|
2015-06-25 14:15:02 +02:00
|
|
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
2015-06-23 10:16:53 +02:00
|
|
|
}
|
2015-06-29 11:44:16 +02:00
|
|
|
|
|
|
|
if(this.props.isReadyForFormSubmission && this.props.isReadyForFormSubmission(this.state.filesToUpload)) {
|
|
|
|
// if so, set uploadstatus to true
|
|
|
|
this.props.setUploadStatus(true);
|
|
|
|
} else {
|
|
|
|
this.props.setUploadStatus(false);
|
|
|
|
}
|
2015-06-23 10:16:53 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
onProgress(id, name, uploadedBytes, totalBytes) {
|
|
|
|
var newState = React.addons.update(this.state, {
|
|
|
|
filesToUpload: { [id]: {
|
2015-06-23 13:55:05 +02:00
|
|
|
progress: { $set: (uploadedBytes / totalBytes) * 100} }
|
2015-06-23 10:16:53 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
this.setState(newState);
|
|
|
|
},
|
|
|
|
|
|
|
|
handleDeleteFile(fileId) {
|
|
|
|
// delete file from server
|
|
|
|
this.state.uploader.deleteFile(fileId);
|
2015-06-23 13:55:05 +02:00
|
|
|
// this is being continues in onDeleteFile, as
|
2015-06-23 10:16:53 +02:00
|
|
|
// fineuploaders deleteFile does not return a correct callback or
|
|
|
|
// promise
|
|
|
|
},
|
|
|
|
|
2015-06-29 10:01:54 +02:00
|
|
|
handleCancelFile(fileId) {
|
|
|
|
this.state.uploader.cancel(fileId);
|
|
|
|
},
|
|
|
|
|
2015-06-23 10:16:53 +02:00
|
|
|
handleUploadFile(files) {
|
2015-06-25 11:12:40 +02:00
|
|
|
|
|
|
|
// If multiple set and user already uploaded its work,
|
|
|
|
// cancel upload
|
2015-06-29 11:01:45 +02:00
|
|
|
if(!this.props.multiple && this.state.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled').length > 0) {
|
2015-06-25 11:12:40 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if multiple is set to false and user drops multiple files into the dropzone,
|
|
|
|
// take the first one and notify user that only one file can be submitted
|
|
|
|
if(!this.props.multiple && files.length > 1) {
|
|
|
|
let tempFilesList = [];
|
|
|
|
tempFilesList.push(files[0]);
|
|
|
|
|
|
|
|
// replace filelist with first-element file list
|
|
|
|
files = tempFilesList;
|
|
|
|
|
|
|
|
let notification = new GlobalNotificationModel('Only one file allowed (took first one)', 'danger', 10000);
|
|
|
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
|
|
|
}
|
|
|
|
|
2015-06-23 10:16:53 +02:00
|
|
|
this.state.uploader.addFiles(files);
|
|
|
|
let oldFiles = this.state.filesToUpload;
|
|
|
|
let oldAndNewFiles = this.state.uploader.getUploads();
|
|
|
|
|
|
|
|
// Add fineuploader specific information to new files
|
|
|
|
for(let i = 0; i < oldAndNewFiles.length; i++) {
|
|
|
|
for(let j = 0; j < files.length; j++) {
|
|
|
|
if(oldAndNewFiles[i].originalName === files[j].name) {
|
|
|
|
oldAndNewFiles[i].progress = 0;
|
|
|
|
oldAndNewFiles[i].type = files[j].type;
|
|
|
|
oldAndNewFiles[i].url = URL.createObjectURL(files[j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// and re-add fineuploader specific information for old files as well
|
|
|
|
for(let i = 0; i < oldAndNewFiles.length; i++) {
|
|
|
|
for(let j = 0; j < oldFiles.length; j++) {
|
|
|
|
if(oldAndNewFiles[i].originalName === oldFiles[j].name) {
|
2015-06-23 13:55:05 +02:00
|
|
|
oldAndNewFiles[i].progress = oldFiles[j].progress;
|
2015-06-23 10:16:53 +02:00
|
|
|
oldAndNewFiles[i].type = oldFiles[j].type;
|
|
|
|
oldAndNewFiles[i].url = oldFiles[j].url;
|
2015-06-29 16:00:26 +02:00
|
|
|
oldAndNewFiles[i].key = oldFiles[j].key;
|
2015-06-23 10:16:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-29 11:01:45 +02:00
|
|
|
// set the new file array
|
2015-06-23 10:16:53 +02:00
|
|
|
let newState = React.addons.update(this.state, {
|
|
|
|
filesToUpload: { $set: oldAndNewFiles }
|
|
|
|
});
|
|
|
|
this.setState(newState);
|
|
|
|
},
|
|
|
|
|
2015-06-29 10:01:54 +02:00
|
|
|
removeFileWithIdFromFilesToUpload(fileId) {
|
|
|
|
// also, sync files from state with the ones from fineuploader
|
|
|
|
let filesToUpload = JSON.parse(JSON.stringify(this.state.filesToUpload));
|
|
|
|
|
|
|
|
// splice because I can
|
|
|
|
filesToUpload.splice(fileId, 1);
|
|
|
|
|
|
|
|
// set state
|
|
|
|
let newState = React.addons.update(this.state, {
|
|
|
|
filesToUpload: { $set: filesToUpload }
|
|
|
|
});
|
|
|
|
this.setState(newState);
|
|
|
|
},
|
|
|
|
|
2015-06-23 10:16:53 +02:00
|
|
|
render() {
|
|
|
|
return (
|
2015-06-29 16:00:26 +02:00
|
|
|
<div>
|
|
|
|
<FileDragAndDrop
|
|
|
|
className="file-drag-and-drop"
|
|
|
|
onDrop={this.handleUploadFile}
|
|
|
|
filesToUpload={this.state.filesToUpload}
|
|
|
|
handleDeleteFile={this.handleDeleteFile}
|
|
|
|
handleCancelFile={this.handleCancelFile}
|
|
|
|
multiple={this.props.multiple}
|
|
|
|
dropzoneInactive={!this.props.multiple && this.state.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled').length > 0} />
|
|
|
|
<FileDragAndDropToolbar />
|
|
|
|
</div>
|
2015-06-23 10:16:53 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
export default ReactS3FineUploader;
|