Remove old uploaders

This commit is contained in:
Brett Sun 2016-07-08 16:19:35 +02:00
parent 819b367896
commit 1069121e07
13 changed files with 1 additions and 2465 deletions

View File

@ -1,119 +0,0 @@
'use strict';
import React from 'react';
import Property from './../ascribe_forms/property';
import ReactS3FineUploader from './../ascribe_uploader/react_s3_fine_uploader';
import AppConstants from '../../constants/application_constants';
import { ValidationTypes } from '../../constants/uploader_constants';
import { makeCsrfHeader } from '../../utils/csrf';
import { getLangText } from '../../utils/lang';
import { resolveUrl } from '../../utils/url_resolver';
const { func, bool, number, object, string, arrayOf } = React.PropTypes;
let FurtherDetailsFileuploader = React.createClass({
propTypes: {
pieceId: number.isRequired,
editable: bool,
label: string,
otherData: arrayOf(object),
// Props for ReactS3FineUploader
areAssetsDownloadable: bool,
isReadyForFormSubmission: func,
submitFile: func, // TODO: rename to onSubmitFile
onValidationFailed: func,
multiple: bool,
setIsUploadReady: func, //TODO: rename to setIsUploaderValidated
showErrorPrompt: bool,
validation: ReactS3FineUploader.propTypes.validation
},
getDefaultProps() {
return {
areAssetsDownloadable: true,
label: getLangText('Additional files'),
multiple: false,
validation: ValidationTypes.additionalData
};
},
render() {
const { editable,
isReadyForFormSubmission,
multiple,
onValidationFailed,
otherData,
pieceId,
setIsUploadReady,
showErrorPrompt,
submitFile,
validation } = this.props;
// Essentially there a three cases important to the fileuploader
//
// 1. there is no other_data => do not show the fileuploader at all (where otherData is now an array)
// 2. there is other_data, but user has no edit rights => show fileuploader but without action buttons
// 3. both other_data and editable are defined or true => show fileuploader with all action buttons
if (!editable && (!otherData || otherData.length === 0)) {
return null;
}
let otherDataIds = otherData ? otherData.map((data) => data.id).join() : null;
return (
<Property
name="other_data_key"
label={this.props.label}>
<ReactS3FineUploader
areAssetsDownloadable
areAssetsEditable={editable}
createBlobRoutine={{
url: resolveUrl('blob_otherdatas'),
pieceId: pieceId
}}
deleteFile={{
enabled: true,
method: 'DELETE',
endpoint: `${AppConstants.serverUrl}/s3/delete`,
customHeaders: makeCsrfHeader()
}}
isReadyForFormSubmission={isReadyForFormSubmission}
keyRoutine={{
url: `${AppConstants.serverUrl}/s3/key/`,
fileClass: 'otherdata',
pieceId: pieceId
}}
multiple={multiple}
onValidationFailed={onValidationFailed}
setIsUploadReady={setIsUploadReady}
session={{
endpoint: `${AppConstants.serverUrl}/api/blob/otherdatas/fineuploader_session/`,
customHeaders: makeCsrfHeader(),
params: {
'pk': otherDataIds
},
cors: {
expected: true,
sendCredentials: true
}
}}
signature={{
endpoint: `${AppConstants.serverUrl}/s3/signature/`,
customHeaders: makeCsrfHeader()
}}
submitFile={submitFile}
showErrorPrompt={showErrorPrompt}
validation={validation} />
</Property>
);
}
});
export default FurtherDetailsFileuploader;

View File

@ -1,141 +0,0 @@
'use strict';
import React from 'react';
import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader';
import FileDragAndDrop from '../ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop';
import AppConstants from '../../constants/application_constants';
import { makeCsrfHeader } from '../../utils/csrf';
const { func, bool, oneOf } = React.PropTypes;
const InputFineUploader = React.createClass({
propTypes: {
// isFineUploaderActive is used to lock react fine uploader in case
// a user is actually not logged in already to prevent him from droping files
// before login in
isFineUploaderActive: bool,
// provided by Property
disabled: bool,
onChange: func,
// Props for ReactS3FineUploader
areAssetsDownloadable: bool,
createBlobRoutine: ReactS3FineUploader.propTypes.createBlobRoutine,
enableLocalHashing: bool,
fileClassToUpload: ReactS3FineUploader.propTypes.fileClassToUpload,
fileInputElement: ReactS3FineUploader.propTypes.fileInputElement,
isReadyForFormSubmission: func,
keyRoutine: ReactS3FineUploader.propTypes.keyRoutine,
handleChangedFile: func, // TODO: rename to onChangedFile
submitFile: func, // TODO: rename to onSubmitFile
onValidationFailed: func,
setIsUploadReady: func, //TODO: rename to setIsUploaderValidated
setWarning: func,
showErrorPrompt: bool,
uploadMethod: oneOf(['hash', 'upload']),
validation: ReactS3FineUploader.propTypes.validation,
},
getDefaultProps() {
return {
fileInputElement: FileDragAndDrop
};
},
getInitialState() {
return {
value: null,
file: null
};
},
submitFile(file) {
this.setState({
file,
value: file.key
});
if (this.state.value && typeof this.props.onChange === 'function') {
this.props.onChange({ target: { value: this.state.value } });
}
if (typeof this.props.submitFile === 'function') {
this.props.submitFile(file);
}
},
reset() {
this.refs.fineuploader.reset();
},
createBlobRoutine() {
const { fineuploader } = this.refs;
const { file } = this.state;
fineuploader.createBlob(file);
},
render() {
const { areAssetsDownloadable,
createBlobRoutine,
enableLocalHashing,
disabled,
fileClassToUpload,
fileInputElement,
handleChangedFile,
isFineUploaderActive,
isReadyForFormSubmission,
keyRoutine,
onValidationFailed,
setIsUploadReady,
setWarning,
showErrorPrompt,
uploadMethod,
validation } = this.props;
let editable = isFineUploaderActive;
// if disabled is actually set by property, we want to override
// isFineUploaderActive
if (typeof disabled !== 'undefined') {
editable = !disabled;
}
return (
<ReactS3FineUploader
ref="fineuploader"
fileInputElement={fileInputElement}
keyRoutine={keyRoutine}
createBlobRoutine={createBlobRoutine}
validation={validation}
submitFile={this.submitFile}
onValidationFailed={onValidationFailed}
setIsUploadReady={setIsUploadReady}
isReadyForFormSubmission={isReadyForFormSubmission}
areAssetsDownloadable={areAssetsDownloadable}
areAssetsEditable={editable}
setWarning={setWarning}
showErrorPrompt={showErrorPrompt}
signature={{
endpoint: `${AppConstants.serverUrl}/s3/signature/`,
customHeaders: makeCsrfHeader()
}}
deleteFile={{
enabled: true,
method: 'DELETE',
endpoint: `${AppConstants.serverUrl}/s3/delete`,
customHeaders: makeCsrfHeader()
}}
enableLocalHashing={enableLocalHashing}
uploadMethod={uploadMethod}
fileClassToUpload={fileClassToUpload}
handleChangedFile={handleChangedFile} />
);
}
});
export default InputFineUploader;

View File

@ -1,253 +0,0 @@
'use strict';
import React from 'react';
import classNames from 'classnames';
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
import FileDragAndDropDialog from './file_drag_and_drop_dialog';
import FileDragAndDropErrorDialog from './file_drag_and_drop_error_dialog';
import FileDragAndDropPreviewIterator from './file_drag_and_drop_preview_iterator';
import { FileStatus } from '../react_s3_fine_uploader_utils';
import { getLangText } from '../../../utils/lang';
// Taken from: https://github.com/fedosejev/react-file-drag-and-drop
let FileDragAndDrop = React.createClass({
propTypes: {
areAssetsDownloadable: React.PropTypes.bool,
areAssetsEditable: React.PropTypes.bool,
multiple: React.PropTypes.bool,
dropzoneInactive: React.PropTypes.bool,
filesToUpload: React.PropTypes.array,
onDrop: React.PropTypes.func.isRequired,
onDragOver: React.PropTypes.func,
handleDeleteFile: React.PropTypes.func,
handleCancelFile: React.PropTypes.func,
handlePauseFile: React.PropTypes.func,
handleResumeFile: React.PropTypes.func,
handleRetryFiles: React.PropTypes.func,
enableLocalHashing: React.PropTypes.bool,
uploadMethod: React.PropTypes.string,
// triggers a FileDragAndDrop-global spinner
hashingProgress: React.PropTypes.number,
// sets the value of this.state.hashingProgress in reactfineuploader
// to -1 which is code for: aborted
handleCancelHashing: React.PropTypes.func,
showError: React.PropTypes.bool,
errorClass: React.PropTypes.shape({
name: React.PropTypes.string,
prettifiedText: React.PropTypes.string
}),
// A class of a file the user has to upload
// Needs to be defined both in singular as well as in plural
fileClassToUpload: React.PropTypes.shape({
singular: React.PropTypes.string,
plural: React.PropTypes.string
}),
allowedExtensions: React.PropTypes.string
},
clearSelection() {
this.refs.fileSelector.value = '';
},
handleDragOver(event) {
event.preventDefault();
if (typeof this.props.onDragOver === 'function') {
this.props.onDragOver(event);
}
},
handleDrop(event) {
event.preventDefault();
event.stopPropagation();
if (!this.props.dropzoneInactive) {
let files;
// handle Drag and Drop
if(event.dataTransfer && event.dataTransfer.files.length > 0) {
files = event.dataTransfer.files;
} else if(event.target.files) { // handle input type file
files = event.target.files;
}
if(typeof this.props.onDrop === 'function' && files) {
this.props.onDrop(files);
}
}
},
handleDeleteFile(fileId) {
// input's value is not changed the second time someone
// inputs the same file again, therefore we need to reset its value
this.clearSelection();
this.props.handleDeleteFile(fileId);
},
handleCancelFile(fileId) {
// input's value is not changed the second time someone
// inputs the same file again, therefore we need to reset its value
this.clearSelection();
this.props.handleCancelFile(fileId);
},
handlePauseFile(fileId) {
// input's value is not changed the second time someone
// inputs the same file again, therefore we need to reset its value
this.clearSelection();
this.props.handlePauseFile(fileId);
},
handleResumeFile(fileId) {
// input's value is not changed the second time someone
// inputs the same file again, therefore we need to reset its value
this.clearSelection();
this.props.handleResumeFile(fileId);
},
handleOnClick() {
// do not propagate event if the drop zone's inactive,
// for example when multiple is set to false and the user already uploaded a piece
if (!this.props.dropzoneInactive) {
let evt;
try {
evt = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
});
} catch(e) {
// For browsers that do not support the new MouseEvent syntax
evt = document.createEvent('MouseEvents');
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
}
evt.stopPropagation();
this.refs.fileSelector.dispatchEvent(evt);
}
},
getErrorDialog(failedFiles) {
const { errorClass } = this.props;
return (
<FileDragAndDropErrorDialog
errorClass={errorClass}
files={failedFiles}
handleRetryFiles={this.props.handleRetryFiles} />
);
},
getPreviewIterator() {
const { areAssetsDownloadable, areAssetsEditable, filesToUpload } = this.props;
return (
<FileDragAndDropPreviewIterator
files={filesToUpload}
handleDeleteFile={this.handleDeleteFile}
handleCancelFile={this.handleCancelFile}
handlePauseFile={this.handlePauseFile}
handleResumeFile={this.handleResumeFile}
areAssetsDownloadable={areAssetsDownloadable}
areAssetsEditable={areAssetsEditable} />
);
},
getUploadDialog() {
const { enableLocalHashing, fileClassToUpload, multiple, uploadMethod } = this.props;
return (
<FileDragAndDropDialog
multipleFiles={multiple}
onClick={this.handleOnClick}
enableLocalHashing={enableLocalHashing}
uploadMethod={uploadMethod}
fileClassToUpload={fileClassToUpload} />
);
},
render: function () {
const { allowedExtensions,
dropzoneInactive,
errorClass,
filesToUpload,
handleCancelHashing,
hashingProgress,
multiple,
showError } = this.props;
// has files only is true if there are files that do not have the status deleted, canceled, or failed
const hasFiles = filesToUpload
.filter((file) => {
return file.status !== FileStatus.DELETED &&
file.status !== FileStatus.CANCELED &&
file.status !== FileStatus.UPLOAD_FAILED &&
file.size !== -1;
})
.length > 0;
const failedFiles = filesToUpload.filter((file) => file.status === FileStatus.UPLOAD_FAILED);
let hasError = showError && errorClass && failedFiles.length > 0;
// if !== -2: triggers a FileDragAndDrop-global spinner
if (hashingProgress !== -2) {
return (
<div className="file-drag-and-drop-hashing-dialog">
<p>{getLangText('Computing hash(es)... This may take a few minutes.')}</p>
<p>
<a onClick={handleCancelHashing}> {getLangText('Cancel hashing')}</a>
</p>
<ProgressBar
now={Math.ceil(hashingProgress)}
label="%(percent)s%"
className="ascribe-progress-bar"/>
</div>
);
} else {
return (
<div
className={classNames('file-drag-and-drop', dropzoneInactive ? 'inactive-dropzone' : 'active-dropzone', { 'has-files': hasFiles })}
onDrag={this.handleDrop}
onDragOver={this.handleDragOver}
onDrop={this.handleDrop}>
{hasError ? this.getErrorDialog(failedFiles) : this.getPreviewIterator()}
{!hasFiles && !hasError ? this.getUploadDialog() : null}
{/*
Opera doesn't trigger simulated click events
if the targeted input has `display:none` set.
Which means we need to set its visibility to hidden
instead of using `display:none`.
See:
- http://stackoverflow.com/questions/12880604/jquery-triggerclick-not-working-on-opera-if-the-element-is-not-displayed
*/}
<input
multiple={multiple}
ref="fileSelector"
type="file"
style={{
visibility: 'hidden',
position: 'absolute',
top: 0,
height: 0,
width: 0
}}
onChange={this.handleDrop}
accept={allowedExtensions}/>
</div>
);
}
}
});
export default FileDragAndDrop;

View File

@ -1,123 +0,0 @@
'use strict';
import React from 'react';
import { Link } from 'react-router';
import { dragAndDropAvailable } from 'js-utility-belt/es6/feature_detection';
import { getLangText } from '../../../utils/lang';
import { getCurrentQueryParams } from '../../../utils/url';
let FileDragAndDropDialog = React.createClass({
propTypes: {
multipleFiles: React.PropTypes.bool,
enableLocalHashing: React.PropTypes.bool,
uploadMethod: React.PropTypes.string,
onClick: React.PropTypes.func,
// A class of a file the user has to upload
// Needs to be defined both in singular as well as in plural
fileClassToUpload: React.PropTypes.shape({
singular: React.PropTypes.string,
plural: React.PropTypes.string
})
},
getDragDialog(fileClass) {
if (dragAndDropAvailable) {
return [
<p className="file-drag-and-drop-dialog-title">{getLangText('Drag %s here', fileClass)}</p>,
<p>{getLangText('or')}</p>
];
} else {
return null;
}
},
render() {
const {
enableLocalHashing,
fileClassToUpload,
multipleFiles,
onClick,
uploadMethod
} = this.props;
let dialogElement;
if (enableLocalHashing && !uploadMethod) {
const currentQueryParams = getCurrentQueryParams();
const queryParamsHash = Object.assign({}, currentQueryParams);
queryParamsHash.method = 'hash';
const queryParamsUpload = Object.assign({}, currentQueryParams);
queryParamsUpload.method = 'upload';
dialogElement = (
<div className="present-options">
<p className="file-drag-and-drop-dialog-title">{getLangText('Would you rather')}</p>
{/*
The frontend in live is hosted under /app,
Since `Link` is appending that base url, if its defined
by itself, we need to make sure to not set it at this point.
Otherwise it will be appended twice.
*/}
<Link
to={`/${window.location.pathname.split('/').pop()}`}
query={queryParamsHash}>
<span className="btn btn-default btn-sm">
{getLangText('Hash your work')}
</span>
</Link>
<span> {getLangText('or')} </span>
<Link
to={`/${window.location.pathname.split('/').pop()}`}
query={queryParamsUpload}>
<span className="btn btn-default btn-sm">
{getLangText('Upload and hash your work')}
</span>
</Link>
</div>
);
} else {
if (multipleFiles) {
dialogElement = [
this.getDragDialog(fileClassToUpload.plural),
(<span
key='mutlipleFilesBtn'
className="btn btn-default"
onClick={onClick}>
{getLangText('choose %s to upload', fileClassToUpload.plural)}
</span>)
];
} else {
const dialog = uploadMethod === 'hash' ? getLangText('choose a %s to hash', fileClassToUpload.singular)
: getLangText('choose a %s to upload', fileClassToUpload.singular);
dialogElement = [
this.getDragDialog(fileClassToUpload.singular),
(<span
key='singleFileBtn'
className="btn btn-default"
onClick={onClick}>
{dialog}
</span>)
];
}
}
return (
<div className="file-drag-and-drop-dialog">
<div className="hidden-print">
{dialogElement}
</div>
{/* Hide the uploader and just show that there's been on files uploaded yet when printing */}
<p className="text-align-center visible-print">
{getLangText('No files uploaded')}
</p>
</div>
);
}
});
export default FileDragAndDropDialog;

View File

@ -1,86 +0,0 @@
'use strict';
import React from 'react';
import classNames from 'classnames';
import { ErrorClasses } from '../../../constants/error_constants';
import { getLangText } from '../../../utils/lang';
let FileDragAndDropErrorDialog = React.createClass({
propTypes: {
errorClass: React.PropTypes.shape({
name: React.PropTypes.string,
prettifiedText: React.PropTypes.string
}).isRequired,
files: React.PropTypes.array.isRequired,
handleRetryFiles: React.PropTypes.func.isRequired
},
getRetryButton(text, openIntercom) {
return (
<button
type="button"
className='btn btn-default'
onClick={() => {
if (openIntercom) {
window.Intercom('showNewMessage', getLangText("I'm having trouble uploading my file."));
}
this.retryAllFiles()
}}>
{getLangText(text)}
</button>
);
},
getContactUsDetail() {
return (
<div className='file-drag-and-drop-error'>
<h4>{getLangText('Let us help you')}</h4>
<p>{getLangText('Still having problems? Send us a message.')}</p>
{this.getRetryButton('Contact us', true)}
</div>
);
},
getErrorDetail(multipleFiles) {
const { errorClass: { prettifiedText }, files } = this.props;
return (
<div className='file-drag-and-drop-error'>
<div className={classNames('file-drag-and-drop-error-detail', { 'file-drag-and-drop-error-detail-multiple-files': multipleFiles })}>
<h4>{getLangText(multipleFiles ? 'Some files did not upload correctly'
: 'Error uploading the file!')}
</h4>
<p>{prettifiedText}</p>
{this.getRetryButton('Retry')}
</div>
<span className={classNames('file-drag-and-drop-error-icon-container', { 'file-drag-and-drop-error-icon-container-multiple-files': multipleFiles })}>
<span className='ascribe-icon icon-ascribe-thin-cross'></span>
</span>
<div className='file-drag-and-drop-error-file-names'>
<ul>
{files.map((file) => (<li key={file.id} className='file-name'>{file.originalName}</li>))}
</ul>
</div>
</div>
);
},
retryAllFiles() {
const { files, handleRetryFiles } = this.props;
handleRetryFiles(files.map(file => file.id));
},
render() {
const { errorClass: { name: errorName }, files } = this.props;
const multipleFiles = files.length > 1;
const contactUs = errorName === ErrorClasses.upload.contactUs.name;
return contactUs ? this.getContactUsDetail() : this.getErrorDetail(multipleFiles);
}
});
export default FileDragAndDropErrorDialog;

View File

@ -1,152 +0,0 @@
'use strict';
import React from 'react';
import FileDragAndDropPreviewImage from './file_drag_and_drop_preview_image';
import FileDragAndDropPreviewOther from './file_drag_and_drop_preview_other';
import { FileStatus } from '../react_s3_fine_uploader_utils';
import { extractFileExtensionFromString } from '../../../utils/file';
import { getLangText } from '../../../utils/lang';
import { truncateText } from '../../../utils/text';
const { shape, string, number, func, bool } = React.PropTypes;
const FileDragAndDropPreview = React.createClass({
propTypes: {
file: shape({
url: string,
type: string,
progress: number,
id: number,
status: string,
s3Url: string,
s3UrlSafe: string
}).isRequired,
areAssetsDownloadable: bool,
areAssetsEditable: bool,
handleDeleteFile: func,
handleCancelFile: func,
handlePauseFile: func,
handleResumeFile: func,
numberOfDisplayedFiles: number
},
toggleUploadProcess() {
const { file, handlePauseFile, handleResumeFile } = this.props;
if (file.status === FileStatus.UPLOADING) {
handlePauseFile(file.id);
} else if (file.status === FileStatus.PAUSED) {
handleResumeFile(file.id);
}
},
handleDeleteFile() {
const { file,
handleDeleteFile,
handleCancelFile } = this.props;
// `handleDeleteFile` is optional, so if its not submitted, don't run it
//
// For delete though, we only want to trigger it, when we're sure that
// the file has *completely* been uploaded to S3 and call now also be
// deleted using an HTTP DELETE request.
if (handleDeleteFile &&
file.progress === 100 &&
(file.status === FileStatus.UPLOAD_SUCCESSFUL || file.status === FileStatus.ONLINE) &&
file.s3UrlSafe) {
handleDeleteFile(file.id);
} else if (handleCancelFile) {
handleCancelFile(file.id);
}
},
handleDownloadFile() {
if (this.props.file.s3Url) {
// This simply opens a new browser tab with the url provided
open(this.props.file.s3Url);
}
},
getFileName() {
const { numberOfDisplayedFiles, file } = this.props;
if (numberOfDisplayedFiles === 1) {
return (
<span className="file-name">
{truncateText(file.name, 30, `(...).${extractFileExtensionFromString(file.name)}`)}
</span>
);
} else {
return null;
}
},
getRemoveButton() {
if (this.props.areAssetsEditable) {
return (
<div className="delete-file">
<span
className="glyphicon glyphicon-remove text-center"
aria-hidden="true"
title={getLangText('Remove file')}
onClick={this.handleDeleteFile}/>
</div>
);
} else {
return null;
}
},
render() {
const { file,
areAssetsDownloadable,
numberOfDisplayedFiles } = this.props;
const innerStyle = numberOfDisplayedFiles === 1 ? { verticalAlign: 'middle' } : null;
const outerStyle = numberOfDisplayedFiles !== 1 ? { display: 'inline-block' } : null;
let previewElement;
// Decide whether an image or a placeholder picture should be displayed
// If a file has its `thumbnailUrl` defined, then we display it also as an image
if (file.type.split('/')[0] === 'image' || file.thumbnailUrl) {
previewElement = (
<FileDragAndDropPreviewImage
onClick={this.handleDeleteFile}
progress={file.progress}
url={file.thumbnailUrl || file.url}
toggleUploadProcess={this.toggleUploadProcess}
areAssetsDownloadable={areAssetsDownloadable}
downloadUrl={file.s3UrlSafe}
showProgress={numberOfDisplayedFiles > 1} />
);
} else {
previewElement = (
<FileDragAndDropPreviewOther
onClick={this.handleDeleteFile}
progress={file.progress}
type={extractFileExtensionFromString(file.name)}
toggleUploadProcess={this.toggleUploadProcess}
areAssetsDownloadable={areAssetsDownloadable}
downloadUrl={file.s3UrlSafe}
showProgress={numberOfDisplayedFiles > 1} />
);
}
return (
<div style={outerStyle}>
<div
style={innerStyle}
className="file-drag-and-drop-position">
{this.getRemoveButton()}
{previewElement}
</div>
{this.getFileName()}
</div>
);
}
});
export default FileDragAndDropPreview;

View File

@ -1,85 +0,0 @@
'use strict';
import React from 'react';
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
import AclProxy from '../../acl_proxy';
import AscribeSpinner from '../../ascribe_spinner';
import { getLangText } from '../../../utils/lang';
const { number, string, func, bool } = React.PropTypes;
const FileDragAndDropPreviewImage = React.createClass({
propTypes: {
progress: number,
url: string,
toggleUploadProcess: func,
downloadUrl: string,
areAssetsDownloadable: bool,
showProgress: bool
},
getInitialState() {
return {
paused: true
};
},
toggleUploadProcess(e) {
e.preventDefault();
e.stopPropagation();
this.setState({
paused: !this.state.paused
});
this.props.toggleUploadProcess();
},
render() {
const { url,
progress,
areAssetsDownloadable,
downloadUrl,
showProgress } = this.props;
const imageStyle = {
backgroundImage: 'url("' + url + '")',
backgroundSize: 'cover'
};
let actionSymbol;
// only if assets are actually downloadable, there should be a download icon if the process is already at
// 100%. If not, no actionSymbol should be displayed
if(progress === 100 && areAssetsDownloadable) {
actionSymbol = <a href={downloadUrl} target="_blank" className="glyphicon glyphicon-download action-file" aria-hidden="true" title={getLangText('Download file')}/>;
} else if(progress >= 0 && progress < 100) {
actionSymbol = (
<div className="spinner-file">
<AscribeSpinner color='dark-blue' size='md' />
</div>
);
} else {
actionSymbol = (
<span className='ascribe-icon icon-ascribe-ok action-file'/>
);
}
return (
<div
className="file-drag-and-drop-preview-image hidden-print"
style={imageStyle}>
<AclProxy
show={showProgress}>
<ProgressBar
now={Math.ceil(progress)}
className="ascribe-progress-bar ascribe-progress-bar-xs"/>
</AclProxy>
{actionSymbol}
</div>
);
}
});
export default FileDragAndDropPreviewImage;

View File

@ -1,60 +0,0 @@
'use strict';
import React from 'react';
import FileDragAndDropPreview from './file_drag_and_drop_preview';
import FileDragAndDropPreviewProgress from './file_drag_and_drop_preview_progress';
import { displayValidFilesFilter } from '../react_s3_fine_uploader_utils';
let FileDragAndDropPreviewIterator = React.createClass({
propTypes: {
files: React.PropTypes.array,
handleDeleteFile: React.PropTypes.func,
handleCancelFile: React.PropTypes.func,
handlePauseFile: React.PropTypes.func,
handleResumeFile: React.PropTypes.func,
areAssetsDownloadable: React.PropTypes.bool,
areAssetsEditable: React.PropTypes.bool
},
render() {
let {
files,
handleDeleteFile,
handleCancelFile,
handlePauseFile,
handleResumeFile,
areAssetsDownloadable,
areAssetsEditable
} = this.props;
files = files.filter(displayValidFilesFilter);
if(files && files.length > 0) {
return (
<div>
{files.map((file, i) => {
return (
<FileDragAndDropPreview
key={i}
file={file}
handleDeleteFile={handleDeleteFile}
handleCancelFile={handleCancelFile}
handlePauseFile={handlePauseFile}
handleResumeFile={handleResumeFile}
areAssetsDownloadable={areAssetsDownloadable}
areAssetsEditable={areAssetsEditable}
numberOfDisplayedFiles={files.length}/>
);
})}
<FileDragAndDropPreviewProgress files={files} />
</div>
);
} else {
return null;
}
}
});
export default FileDragAndDropPreviewIterator;

View File

@ -1,87 +0,0 @@
'use strict';
import React from 'react';
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
import AscribeSpinner from '../../ascribe_spinner';
import { getLangText } from '../../../utils/lang';
const { string, number, bool, func } = React.PropTypes;
const FileDragAndDropPreviewOther = React.createClass({
propTypes: {
type: string,
progress: number,
areAssetsDownloadable: bool,
toggleUploadProcess: func,
downloadUrl: string,
showProgress: bool
},
getInitialState() {
return {
paused: true
};
},
toggleUploadProcess(e) {
e.preventDefault();
e.stopPropagation();
this.setState({
paused: !this.state.paused
});
this.props.toggleUploadProcess();
},
render() {
const { progress,
areAssetsDownloadable,
downloadUrl,
type,
showProgress } = this.props;
const style = !showProgress ? { visibility: 'hidden' } : null;
let actionSymbol;
// only if assets are actually downloadable, there should be a
// download icon if the process is already at 100%.
// If not, no actionSymbol should be displayed
if (progress === 100 && areAssetsDownloadable) {
actionSymbol = (
<a
href={downloadUrl}
target="_blank"
className="glyphicon glyphicon-download action-file"
aria-hidden="true"
title={getLangText('Download file')} />
);
} else if (progress >= 0 && progress < 100) {
actionSymbol = (
<div className="spinner-file">
<AscribeSpinner color='dark-blue' size='md' />
</div>
);
} else {
actionSymbol = (
<span className='ascribe-icon icon-ascribe-ok action-file' />
);
}
return (
<div className="file-drag-and-drop-preview">
<ProgressBar
now={Math.ceil(progress)}
style={style}
className="ascribe-progress-bar ascribe-progress-bar-xs" />
<div className="file-drag-and-drop-preview-other">
{actionSymbol}
<p style={style}>{'.' + (type ? type : 'file')}</p>
</div>
</div>
);
}
});
export default FileDragAndDropPreviewOther;

View File

@ -1,58 +0,0 @@
'use strict';
import React from 'react';
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
import { displayValidProgressFilesFilter } from '../react_s3_fine_uploader_utils';
const FileDragAndDropPreviewProgress = React.createClass({
propTypes: {
files: React.PropTypes.array
},
calcOverallFileSize() {
let overallFileSize = 0;
let files = this.props.files.filter(displayValidProgressFilesFilter);
// We just sum up all files' sizes
for(let i = 0; i < files.length; i++) {
overallFileSize += files[i].size;
}
return overallFileSize;
},
calcOverallProgress() {
let overallProgress = 0;
let overallFileSize = this.calcOverallFileSize();
let files = this.props.files.filter(displayValidProgressFilesFilter);
// We calculate the overall progress by summing the individuals
// files' progresses in relation to their size
for(let i = 0; i < files.length; i++) {
overallProgress += files[i].size / overallFileSize * files[i].progress;
}
return overallProgress;
},
render() {
const files = this.props.files.filter(displayValidProgressFilesFilter);
const style = !files.length ? { display: 'none' } : null;
let overallProgress = this.calcOverallProgress();
return (
<div style={{marginTop: '1.3em'}}>
<ProgressBar
now={Math.ceil(overallProgress)}
label={'%(percent)s%'}
className="ascribe-progress-bar"
style={style} />
</div>
);
}
});
export default FileDragAndDropPreviewProgress;

View File

@ -1,188 +0,0 @@
'use strict';
import React from 'react';
import classNames from 'classnames';
import { displayValidProgressFilesFilter, FileStatus } from '../react_s3_fine_uploader_utils';
import { getLangText } from '../../../utils/lang';
import { truncateText } from '../../../utils/text';
const { func, array, bool, shape, string } = React.PropTypes;
export default function UploadButton({ className = 'btn btn-default btn-sm', showLabel = true } = {}) {
return React.createClass({
displayName: 'UploadButton',
propTypes: {
onDrop: func.isRequired,
filesToUpload: array,
multiple: bool,
// For simplification purposes we're just going to use this prop as a
// label for the upload button
fileClassToUpload: shape({
singular: string,
plural: string
}),
allowedExtensions: string,
// provided by ReactS3FineUploader
handleCancelFile: func,
handleDeleteFile: func
},
getInitialState() {
return {
disabled: this.getUploadingFiles().length !== 0
};
},
componentWillReceiveProps(nextProps) {
if(this.props.filesToUpload !== nextProps.filesToUpload) {
this.setState({
disabled: this.getUploadingFiles(nextProps.filesToUpload).length !== 0
});
}
},
handleDrop(event) {
event.preventDefault();
event.stopPropagation();
let files = event.target.files;
if(typeof this.props.onDrop === 'function' && files) {
this.props.onDrop(files);
}
},
getUploadingFiles(filesToUpload = this.props.filesToUpload) {
return filesToUpload.filter((file) => file.status === FileStatus.UPLOADING);
},
getUploadedFile() {
return this.props.filesToUpload.filter((file) => file.status === FileStatus.UPLOAD_SUCCESSFUL)[0];
},
clearSelection() {
this.refs.fileSelector.value = '';
},
handleOnClick() {
if(!this.state.disabled) {
let evt;
// First, remove any currently uploading or uploaded items
this.onClickRemove();
try {
evt = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
});
} catch(e) {
// For browsers that do not support the new MouseEvent syntax
evt = document.createEvent('MouseEvents');
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
}
evt.stopPropagation();
this.refs.fileSelector.dispatchEvent(evt);
}
},
onClickRemove() {
const uploadingFiles = this.getUploadingFiles();
const uploadedFile = this.getUploadedFile();
this.clearSelection();
if(uploadingFiles.length) {
this.props.handleCancelFile(uploadingFiles[0].id);
} else if(uploadedFile && !uploadedFile.s3UrlSafe) {
this.props.handleCancelFile(uploadedFile.id);
} else if(uploadedFile && uploadedFile.s3UrlSafe) {
this.props.handleDeleteFile(uploadedFile.id);
}
},
getButtonLabel() {
let { filesToUpload, fileClassToUpload } = this.props;
// filter invalid files that might have been deleted or canceled...
filesToUpload = filesToUpload.filter(displayValidProgressFilesFilter);
if(this.getUploadingFiles().length !== 0) {
return getLangText('Upload progress') + ': ' + Math.ceil(filesToUpload[0].progress) + '%';
} else {
return fileClassToUpload.singular;
}
},
getUploadedFileLabel() {
if (showLabel) {
const uploadedFile = this.getUploadedFile();
const uploadingFiles = this.getUploadingFiles();
if (uploadingFiles.length) {
return (
<span>
{` ${truncateText(uploadingFiles[0].name, 40)} `}
[<a onClick={this.onClickRemove}>{getLangText('cancel upload')}</a>]
</span>
);
} else if (uploadedFile) {
return (
<span>
<span className='ascribe-icon icon-ascribe-ok'/>
{` ${truncateText(uploadedFile.name, 40)} `}
[<a onClick={this.onClickRemove}>{getLangText('remove')}</a>]
</span>
);
} else {
return <span>{getLangText('No file chosen')}</span>;
}
}
},
render() {
const { allowedExtensions, multiple } = this.props;
const { disabled } = this.state;
/*
* We do not want a button that submits here.
* As UploadButton could be used in forms that want to be submitted independent
* of clicking the selector.
* Therefore the wrapping component needs to be an `anchor` tag instead of a `button`
*/
return (
<div className={classNames('ascribe-upload-button', {'ascribe-upload-button-has-label': showLabel})}>
{/*
The button needs to be of `type="button"` as it would
otherwise submit the form its in.
*/}
<button
type="button"
onClick={this.handleOnClick}
className={className}
disabled={disabled}>
{this.getButtonLabel()}
<input
multiple={multiple}
ref="fileSelector"
type="file"
style={{
display: 'none',
height: 0,
width: 0
}}
onChange={this.handleDrop}
accept={allowedExtensions}/>
</button>
{this.getUploadedFileLabel()}
</div>
);
}
});
}

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,6 @@
'use strict';
import request from '../utils/request';
let S3Fetcher = {
/**
* Fetch the registered applications of a user from the API.
*/
const S3Fetcher = {
deleteFile(key, bucket) {
return request('s3_delete_file', {
method: 'DELETE',