Commit b53d5554 by jkarni

Merge pull request #823 from edx/jkarni/feature/multiple-assets-upload

Add support for multiple file uploads
parents 9c6987d0 d0f72060
...@@ -10,6 +10,16 @@ Feature: Upload Files ...@@ -10,6 +10,16 @@ Feature: Upload Files
Then I should see the file "test" was uploaded Then I should see the file "test" was uploaded
And The url for the file "test" is valid And The url for the file "test" is valid
@skip_safari
Scenario: Users can upload multiple files
Given I have opened a new course in studio
And I go to the files and uploads page
When I upload the files "test","test2"
Then I should see the file "test" was uploaded
And I should see the file "test2" was uploaded
And The url for the file "test2" is valid
And The url for the file "test" is valid
# Uploading isn't working on safari with sauce labs # Uploading isn't working on safari with sauce labs
@skip_safari @skip_safari
Scenario: Users can update files Scenario: Users can update files
......
...@@ -26,7 +26,24 @@ def upload_file(_step, file_name): ...@@ -26,7 +26,24 @@ def upload_file(_step, file_name):
#uploading the file itself #uploading the file itself
path = os.path.join(TEST_ROOT, 'uploads/', file_name) path = os.path.join(TEST_ROOT, 'uploads/', file_name)
world.browser.execute_script("$('input.file-input').css('display', 'block')") world.browser.execute_script("$('input.file-input').css('display', 'block')")
world.browser.attach_file('file', os.path.abspath(path)) world.browser.attach_file('files[]', os.path.abspath(path))
close_css = 'a.close-button'
world.css_click(close_css)
@step(u'I upload the files (".*")$')
def upload_file(_step, files_string):
# Turn files_string to a list of file names
files = files_string.split(",")
files = map(lambda x: string.strip(x, ' "\''), files)
upload_css = 'a.upload-button'
world.css_click(upload_css)
#uploading the files
for f in files:
path = os.path.join(TEST_ROOT, 'uploads/', f)
world.browser.execute_script("$('input.file-input').css('display', 'block')")
world.browser.attach_file('files[]', os.path.abspath(path))
close_css = 'a.close-button' close_css = 'a.close-button'
world.css_click(close_css) world.css_click(close_css)
......
...@@ -149,14 +149,14 @@ def upload_asset(request, org, course, coursename): ...@@ -149,14 +149,14 @@ def upload_asset(request, org, course, coursename):
logging.error('Could not find course' + location) logging.error('Could not find course' + location)
return HttpResponseBadRequest() return HttpResponseBadRequest()
if 'file' not in request.FILES: if 'files[]' not in request.FILES:
return HttpResponseBadRequest() return HttpResponseBadRequest()
# compute a 'filename' which is similar to the location formatting, we're # compute a 'filename' which is similar to the location formatting, we're
# using the 'filename' nomenclature since we're using a FileSystem paradigm # using the 'filename' nomenclature since we're using a FileSystem paradigm
# here. We're just imposing the Location string formatting expectations to # here. We're just imposing the Location string formatting expectations to
# keep things a bit more consistent # keep things a bit more consistent
upload_file = request.FILES['file'] upload_file = request.FILES['files[]']
filename = upload_file.name filename = upload_file.name
mime_type = upload_file.content_type mime_type = upload_file.content_type
......
...@@ -52,7 +52,29 @@ function removeAsset(e){ ...@@ -52,7 +52,29 @@ function removeAsset(e){
function showUploadModal(e) { function showUploadModal(e) {
e.preventDefault(); e.preventDefault();
resetUploadModal();
$modal = $('.upload-modal').show(); $modal = $('.upload-modal').show();
$('.upload-modal .file-chooser').fileupload({
dataType: 'json',
type: 'POST',
maxChunkSize: 100 * 1000 * 1000, // 100 MB
autoUpload: true,
progressall: function(e, data) {
var percentComplete = parseInt((100 * data.loaded) / data.total, 10);
showUploadFeedback(e, percentComplete);
},
maxFileSize: 100 * 1000 * 1000, // 100 MB
maxNumberofFiles: 100,
add: function(e, data) {
data.process().done(function () {
data.submit();
});
},
done: function(e, data) {
displayFinishedUpload(data.result);
}
});
$('.file-input').bind('change', startUpload); $('.file-input').bind('change', startUpload);
$modalCover.show(); $modalCover.show();
} }
...@@ -69,11 +91,6 @@ function startUpload(e) { ...@@ -69,11 +91,6 @@ function startUpload(e) {
$('.upload-modal h1').html(gettext('Uploading…')); $('.upload-modal h1').html(gettext('Uploading…'));
$('.upload-modal .file-name').html(files[0].name); $('.upload-modal .file-name').html(files[0].name);
$('.upload-modal .file-chooser').ajaxSubmit({
beforeSend: resetUploadBar,
uploadProgress: showUploadFeedback,
complete: displayFinishedUpload
});
$('.upload-modal .choose-file-button').hide(); $('.upload-modal .choose-file-button').hide();
$('.upload-modal .progress-bar').removeClass('loaded').show(); $('.upload-modal .progress-bar').removeClass('loaded').show();
} }
...@@ -84,18 +101,28 @@ function resetUploadBar() { ...@@ -84,18 +101,28 @@ function resetUploadBar() {
$('.upload-modal .progress-fill').html(percentVal); $('.upload-modal .progress-fill').html(percentVal);
} }
function showUploadFeedback(event, position, total, percentComplete) { function resetUploadModal() {
// Reset modal so it no longer displays information about previously
// completed uploads.
resetUploadBar();
$('.upload-modal .file-name').html('');
$('.upload-modal h1').html(gettext('Upload New File'));
$('.upload-modal .choose-file-button').html(gettext('Choose File'));
$('.upload-modal .embeddable-xml-input').val('');
$('.upload-modal .embeddable').hide();
}
function showUploadFeedback(event, percentComplete) {
var percentVal = percentComplete + '%'; var percentVal = percentComplete + '%';
$('.upload-modal .progress-fill').width(percentVal); $('.upload-modal .progress-fill').width(percentVal);
$('.upload-modal .progress-fill').html(percentVal); $('.upload-modal .progress-fill').html(percentVal);
} }
function displayFinishedUpload(xhr) { function displayFinishedUpload(resp) {
if (xhr.status == 200) { if (resp.status == 200) {
markAsLoaded(); markAsLoaded();
} }
var resp = JSON.parse(xhr.responseText);
$('.upload-modal .embeddable-xml-input').val(resp.portable_url); $('.upload-modal .embeddable-xml-input').val(resp.portable_url);
$('.upload-modal .embeddable').show(); $('.upload-modal .embeddable').show();
$('.upload-modal .file-name').hide(); $('.upload-modal .file-name').hide();
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
<%block name="jsextra"> <%block name="jsextra">
<script src="${static.url('js/vendor/mustache.js')}"></script> <script src="${static.url('js/vendor/mustache.js')}"></script>
<script src="${static.url('js/vendor/jQuery-File-Upload/js/jquery.iframe-transport.js')}"> </script>
<script src="${static.url('js/vendor/jQuery-File-Upload/js/jquery.fileupload.js')}"> </script>
</%block> </%block>
<%block name="content"> <%block name="content">
...@@ -40,12 +42,12 @@ ...@@ -40,12 +42,12 @@
<div class="wrapper-mast wrapper"> <div class="wrapper-mast wrapper">
<header class="mast has-actions has-subtitle"> <header class="mast has-actions has-subtitle">
<h1 class="page-header"> <h1 class="page-header">
<small class="subtitle">Content</small> <small class="subtitle">${_("Content")}</small>
<span class="sr">&gt; </span>Files &amp; Uploads <span class="sr">&gt; </span>${_("Files &amp; Uploads")}
</h1> </h1>
<nav class="nav-actions"> <nav class="nav-actions">
<h3 class="sr">Page Actions</h3> <h3 class="sr">${_("Page Actions")}</h3>
<ul> <ul>
<li class="nav-item"> <li class="nav-item">
<a href="#" class="button upload-button new-button"><i class="icon-plus"></i> ${_("Upload New File")}</a> <a href="#" class="button upload-button new-button"><i class="icon-plus"></i> ${_("Upload New File")}</a>
...@@ -116,7 +118,7 @@ ...@@ -116,7 +118,7 @@
<div class="upload-modal modal"> <div class="upload-modal modal">
<a href="#" class="close-button"><span class="close-icon"></span></a> <a href="#" class="close-button"><span class="close-icon"></span></a>
<div class="modal-body"> <div class="modal-body">
<h1>Upload New File</h1> <h1>${_("Upload New File")}</h1>
<p class="file-name"></a> <p class="file-name"></a>
<div class="progress-bar"> <div class="progress-bar">
<div class="progress-fill"></div> <div class="progress-fill"></div>
...@@ -127,8 +129,8 @@ ...@@ -127,8 +129,8 @@
</div> </div>
<form class="file-chooser" action="${upload_asset_callback_url}" <form class="file-chooser" action="${upload_asset_callback_url}"
method="post" enctype="multipart/form-data"> method="post" enctype="multipart/form-data">
<a href="#" class="choose-file-button">Choose File</a> <a href="#" class="choose-file-button">${_("Choose File")}</a>
<input type="file" class="file-input" name="file"> <input type="file" class="file-input" name="files[]" multiple>
</form> </form>
</div> </div>
</div> </div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment