Skip to content

Commit 8a70e3e

Browse files
[Document Repository] Upload multiple files at once (#8289)
- <FileElement> now supports the 'multiple' attribute. - The document_repository module has been modified to support multiple uploaded files. - Some visual feedback has been added (error reporting + uploading status). Resolves #8218
1 parent 14ed881 commit 8a70e3e

File tree

6 files changed

+219
-127
lines changed

6 files changed

+219
-127
lines changed

jsx/Form.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1995,7 +1995,7 @@ NumericElement.defaultProps = {
19951995

19961996
/**
19971997
* File Component
1998-
* React wrapper for a simple or 'multiple' <select> element.
1998+
* React wrapper for a simple or 'multiple' <input type="file"> element.
19991999
*/
20002000
class FileElement extends Component {
20012001
/**
@@ -2014,8 +2014,10 @@ class FileElement extends Component {
20142014
*/
20152015
handleChange(e) {
20162016
// Send current file to parent component
2017-
const file = e.target.files[0] ? e.target.files[0] : '';
2018-
this.props.onUserInput(this.props.name, file);
2017+
const files = e.target.files
2018+
? (this.props.allowMultiple ? e.target.files : e.target.files[0])
2019+
: '';
2020+
this.props.onUserInput(this.props.name, files);
20192021
}
20202022

20212023
/**
@@ -2027,14 +2029,20 @@ class FileElement extends Component {
20272029
const required = this.props.required ? 'required' : null;
20282030

20292031
let fileName = undefined;
2032+
20302033
if (this.props.value) {
20312034
switch (typeof this.props.value) {
20322035
case 'string':
20332036
fileName = this.props.value;
20342037
break;
20352038

20362039
case 'object':
2037-
fileName = this.props.value.name;
2040+
if (this.props.value instanceof FileList) {
2041+
const files = this.props.value;
2042+
fileName = Array.from(files).map((file) => file.name).join(', ');
2043+
} else {
2044+
fileName = this.props.value.name;
2045+
}
20382046
break;
20392047

20402048
default:
@@ -2106,6 +2114,7 @@ class FileElement extends Component {
21062114
} else {
21072115
classSz = 'col-sm-12';
21082116
}
2117+
21092118
return (
21102119
<div className={elementClass}>
21112120
{labelHTML}
@@ -2127,6 +2136,7 @@ class FileElement extends Component {
21272136
name={this.props.name}
21282137
onChange={this.handleChange}
21292138
required={required}
2139+
multiple={this.props.allowMultiple}
21302140
/>
21312141
</div>
21322142
</div>
@@ -2148,6 +2158,7 @@ FileElement.propTypes = {
21482158
id: PropTypes.string,
21492159
disabled: PropTypes.bool,
21502160
required: PropTypes.bool,
2161+
allowMultiple: PropTypes.bool,
21512162
hasError: PropTypes.bool,
21522163
errorMessage: PropTypes.string,
21532164
onUserInput: PropTypes.func,
@@ -2160,6 +2171,7 @@ FileElement.defaultProps = {
21602171
id: null,
21612172
disabled: false,
21622173
required: false,
2174+
allowMultiple: false,
21632175
hasError: false,
21642176
errorMessage: 'The field is required!',
21652177
onUserInput: function() {
@@ -2468,7 +2480,7 @@ class ButtonElement extends Component {
24682480
onClick={this.handleClick}
24692481
disabled={this.props.disabled}
24702482
>
2471-
{this.props.label}
2483+
{this.props.disabled ? 'Uploading...' : this.props.label}
24722484
</button>
24732485
</div>
24742486
</div>

modules/document_repository/css/document_repository.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,9 @@
55
.tool:hover + .tip {
66
visibility: visible;
77
}
8+
9+
.upload-response {
10+
max-height: 400px;
11+
overflow-y: scroll;
12+
text-align: left;
13+
}

modules/document_repository/jsx/uploadForm.js

Lines changed: 77 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Loader from 'Loader';
88
* Media Upload Form
99
*
1010
* Fetches data from Loris backend and displays a form allowing
11-
* to upload a document file
11+
* to upload document files
1212
*
1313
* @author Shen Wang
1414
* @version 1.0.0
@@ -28,10 +28,11 @@ class DocUploadForm extends Component {
2828
uploadResult: null,
2929
errorMessage: null,
3030
isLoaded: false,
31+
uploadInProgress: false,
3132
};
3233

3334
this.setFormData = this.setFormData.bind(this);
34-
this.uploadFile = this.uploadFile.bind(this);
35+
this.uploadFiles = this.uploadFiles.bind(this);
3536
this.fetchData = this.fetchData.bind(this);
3637
}
3738

@@ -90,10 +91,10 @@ class DocUploadForm extends Component {
9091
<FormElement
9192
name="docUpload"
9293
fileUpload={true}
93-
onSubmit={this.uploadFile}
94+
onSubmit={this.uploadFiles}
9495
method="POST"
9596
>
96-
<h3>Upload a file</h3><br/>
97+
<h3>Upload files</h3><br/>
9798
<SelectElement
9899
name="category"
99100
label="Category"
@@ -145,14 +146,18 @@ class DocUploadForm extends Component {
145146
value={this.state.formData.comments}
146147
/>
147148
<FileElement
148-
name="file"
149+
name="files"
149150
id="docUploadEl"
150151
onUserInput={this.setFormData}
151-
label="File to upload"
152+
label="File(s) to upload"
152153
required={true}
153-
value={this.state.formData.file}
154+
value={this.state.formData.files}
155+
allowMultiple={true}
156+
/>
157+
<ButtonElement
158+
label="Upload File(s)"
159+
disabled={this.state.uploadInProgress}
154160
/>
155-
<ButtonElement label="Upload File"/>
156161
</FormElement>
157162
</div>
158163
</div>
@@ -166,46 +171,81 @@ class DocUploadForm extends Component {
166171
*/
167172

168173
/**
169-
* Upload file
174+
* Upload file(s)
170175
*/
171-
uploadFile() {
176+
uploadFiles() {
172177
// Set form data and upload the media file
173-
let formData = this.state.formData;
174-
let formObject = new FormData();
175-
for (let key in formData) {
176-
if (formData[key] !== '') {
177-
formObject.append(key, formData[key]);
178+
try {
179+
this.setState({uploadInProgress: true});
180+
let formData = this.state.formData;
181+
let formObject = new FormData();
182+
for (let key in formData) {
183+
if (formData[key] !== '') {
184+
if (key === 'files' &&
185+
document.querySelector('.fileUpload').multiple) {
186+
Array.from(formData[key]).forEach((file) => {
187+
formObject.append('files[]', file);
188+
});
189+
} else {
190+
formObject.append(key, formData[key]);
191+
}
192+
}
178193
}
179-
}
180194

181-
fetch(this.props.action, {
182-
method: 'POST',
183-
cache: 'no-cache',
184-
credentials: 'same-origin',
185-
body: formObject,
186-
})
187-
.then((resp) => {
188-
console.error(resp);
189-
if (resp.ok) {
190-
swal.fire('Upload Successful!', '', 'success').then((result) => {
191-
if (result.value) {
192-
this.setState({formData: {}});
193-
this.props.refreshPage();
195+
fetch(this.props.action, {
196+
method: 'POST',
197+
cache: 'no-cache',
198+
credentials: 'same-origin',
199+
body: formObject,
200+
})
201+
.then((resp) => {
202+
if (resp.ok) {
203+
resp.json().then((data) => {
204+
if (data.error_count === 0) {
205+
swal.fire('Upload Successful!', '', 'success')
206+
.then((result) => {
207+
if (result.value) {
208+
this.setState({formData: {}});
209+
this.props.refreshPage();
210+
}
211+
});
212+
} else {
213+
console.error(resp);
214+
swal.fire('Upload Incomplete', data.message, 'warning');
215+
}
216+
}).catch((error) => {
217+
console.error(error);
218+
swal.fire(
219+
'Error reading response',
220+
'Please report the issue or contact your administrator',
221+
'error'
222+
);
223+
});
224+
} else {
225+
resp.json().then((data) => {
226+
console.error(resp);
227+
swal.fire('Could not upload files', data.error, 'error');
228+
}).catch((error) => {
229+
console.error(error);
230+
swal.fire(
231+
'Error reading error response',
232+
'Please report the issue or contact your administrator',
233+
'error'
234+
);
235+
});
194236
}
195-
});
196-
} else {
197-
resp.json().then((data) => {
198-
swal.fire('Could not upload file', data.error, 'error');
199237
}).catch((error) => {
200238
console.error(error);
201239
swal.fire(
202-
'Unknown Error',
240+
'Something went wrong',
203241
'Please report the issue or contact your administrator',
204242
'error'
205243
);
206-
});
207-
}
208-
});
244+
}).finally(() => this.setState({uploadInProgress: false}));
245+
} catch (error) {
246+
console.error(error);
247+
this.setState({uploadInProgress: false});
248+
}
209249
}
210250

211251
/**

0 commit comments

Comments
 (0)