Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
5c433ec9
Commit
5c433ec9
authored
Dec 11, 2014
by
Sarina Canelake
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #5731 from Stanford-Online/ataki/upstream
Limit Upload File Sizes to GridFS
parents
b1e5002b
fb9320af
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
295 additions
and
62 deletions
+295
-62
AUTHORS
+2
-2
cms/djangoapps/contentstore/views/assets.py
+31
-0
cms/djangoapps/contentstore/views/tests/test_assets.py
+31
-2
cms/envs/common.py
+10
-0
cms/static/coffee/spec/main.coffee
+9
-1
cms/static/coffee/spec/main_squire.coffee
+9
-1
cms/static/coffee/spec/views/assets_spec.coffee
+51
-32
cms/static/js/factories/asset_index.js
+9
-3
cms/static/js/spec/views/assets_spec.js
+51
-2
cms/static/js/views/assets.js
+68
-17
cms/static/js_test.yml
+4
-0
cms/static/js_test_squire.yml
+4
-0
cms/static/require-config.js
+9
-1
cms/templates/asset_index.html
+7
-1
No files found.
AUTHORS
View file @
5c433ec9
...
@@ -180,4 +180,5 @@ Eugeny Kolpakov <eugeny.kolpakov@gmail.com>
...
@@ -180,4 +180,5 @@ Eugeny Kolpakov <eugeny.kolpakov@gmail.com>
Omar Al-Ithawi <oithawi@qrf.org>
Omar Al-Ithawi <oithawi@qrf.org>
Louis Pilfold <louis@lpil.uk>
Louis Pilfold <louis@lpil.uk>
Akiva Leffert <akiva@edx.org>
Akiva Leffert <akiva@edx.org>
Mike Bifulco <mbifulco@aquent.com>
Mike Bifulco <mbifulco@aquent.com>
\ No newline at end of file
Jim Zheng <jimzheng@stanford.edu>
cms/djangoapps/contentstore/views/assets.py
View file @
5c433ec9
...
@@ -83,6 +83,9 @@ def _asset_index(request, course_key):
...
@@ -83,6 +83,9 @@ def _asset_index(request, course_key):
return
render_to_response
(
'asset_index.html'
,
{
return
render_to_response
(
'asset_index.html'
,
{
'context_course'
:
course_module
,
'context_course'
:
course_module
,
'max_file_size_in_mbs'
:
settings
.
MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB
,
'chunk_size_in_mbs'
:
settings
.
UPLOAD_CHUNK_SIZE_IN_MB
,
'max_file_size_redirect_url'
:
settings
.
MAX_ASSET_UPLOAD_FILE_SIZE_URL
,
'asset_callback_url'
:
reverse_course_url
(
'assets_handler'
,
course_key
)
'asset_callback_url'
:
reverse_course_url
(
'assets_handler'
,
course_key
)
})
})
...
@@ -152,6 +155,14 @@ def _get_assets_for_page(request, course_key, current_page, page_size, sort):
...
@@ -152,6 +155,14 @@ def _get_assets_for_page(request, course_key, current_page, page_size, sort):
)
)
def
get_file_size
(
upload_file
):
"""
Helper method for getting file size of an upload file.
Can be used for mocking test file sizes.
"""
return
upload_file
.
size
@require_POST
@require_POST
@ensure_csrf_cookie
@ensure_csrf_cookie
@login_required
@login_required
...
@@ -176,6 +187,26 @@ def _upload_asset(request, course_key):
...
@@ -176,6 +187,26 @@ def _upload_asset(request, course_key):
upload_file
=
request
.
FILES
[
'file'
]
upload_file
=
request
.
FILES
[
'file'
]
filename
=
upload_file
.
name
filename
=
upload_file
.
name
mime_type
=
upload_file
.
content_type
mime_type
=
upload_file
.
content_type
size
=
get_file_size
(
upload_file
)
# If file is greater than a specified size, reject the upload
# request and send a message to the user. Note that since
# the front-end may batch large file uploads in smaller chunks,
# we validate the file-size on the front-end in addition to
# validating on the backend. (see cms/static/js/views/assets.js)
max_file_size_in_bytes
=
settings
.
MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB
*
1000
**
2
if
size
>
max_file_size_in_bytes
:
return
JsonResponse
({
'error'
:
_
(
'File {filename} exceeds maximum size of '
'{size_mb} MB. Please follow the instructions here '
'to upload a file elsewhere and link to it instead: '
'{faq_url}'
)
.
format
(
filename
=
filename
,
size_mb
=
settings
.
MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB
,
faq_url
=
settings
.
MAX_ASSET_UPLOAD_FILE_SIZE_URL
,
)
},
status
=
413
)
content_loc
=
StaticContent
.
compute_location
(
course_key
,
filename
)
content_loc
=
StaticContent
.
compute_location
(
course_key
,
filename
)
...
...
cms/djangoapps/contentstore/views/tests/test_assets.py
View file @
5c433ec9
...
@@ -5,6 +5,7 @@ from datetime import datetime
...
@@ -5,6 +5,7 @@ from datetime import datetime
from
io
import
BytesIO
from
io
import
BytesIO
from
pytz
import
UTC
from
pytz
import
UTC
import
json
import
json
from
django.conf
import
settings
from
contentstore.tests.utils
import
CourseTestCase
from
contentstore.tests.utils
import
CourseTestCase
from
contentstore.views
import
assets
from
contentstore.views
import
assets
from
contentstore.utils
import
reverse_course_url
from
contentstore.utils
import
reverse_course_url
...
@@ -16,10 +17,14 @@ from xmodule.modulestore.django import modulestore
...
@@ -16,10 +17,14 @@ from xmodule.modulestore.django import modulestore
from
xmodule.modulestore.xml_importer
import
import_from_xml
from
xmodule.modulestore.xml_importer
import
import_from_xml
from
django.test.utils
import
override_settings
from
django.test.utils
import
override_settings
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
,
AssetLocation
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
,
AssetLocation
from
django.conf
import
settings
import
mock
from
ddt
import
ddt
from
ddt
import
data
TEST_DATA_DIR
=
settings
.
COMMON_TEST_DATA_ROOT
TEST_DATA_DIR
=
settings
.
COMMON_TEST_DATA_ROOT
MAX_FILE_SIZE
=
settings
.
MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB
*
1000
**
2
class
AssetsTestCase
(
CourseTestCase
):
class
AssetsTestCase
(
CourseTestCase
):
"""
"""
...
@@ -33,9 +38,14 @@ class AssetsTestCase(CourseTestCase):
...
@@ -33,9 +38,14 @@ class AssetsTestCase(CourseTestCase):
"""
"""
Post to the asset upload url
Post to the asset upload url
"""
"""
f
=
self
.
get_sample_asset
(
name
)
return
self
.
client
.
post
(
self
.
url
,
{
"name"
:
name
,
"file"
:
f
})
def
get_sample_asset
(
self
,
name
):
"""Returns an in-memory file with the given name for testing"""
f
=
BytesIO
(
name
)
f
=
BytesIO
(
name
)
f
.
name
=
name
+
".txt"
f
.
name
=
name
+
".txt"
return
self
.
client
.
post
(
self
.
url
,
{
"name"
:
name
,
"file"
:
f
})
return
f
class
BasicAssetsTestCase
(
AssetsTestCase
):
class
BasicAssetsTestCase
(
AssetsTestCase
):
...
@@ -132,6 +142,7 @@ class PaginationTestCase(AssetsTestCase):
...
@@ -132,6 +142,7 @@ class PaginationTestCase(AssetsTestCase):
self
.
assertGreaterEqual
(
name2
,
name3
)
self
.
assertGreaterEqual
(
name2
,
name3
)
@ddt
class
UploadTestCase
(
AssetsTestCase
):
class
UploadTestCase
(
AssetsTestCase
):
"""
"""
Unit tests for uploading a file
Unit tests for uploading a file
...
@@ -148,6 +159,24 @@ class UploadTestCase(AssetsTestCase):
...
@@ -148,6 +159,24 @@ class UploadTestCase(AssetsTestCase):
resp
=
self
.
client
.
post
(
self
.
url
,
{
"name"
:
"file.txt"
},
"application/json"
)
resp
=
self
.
client
.
post
(
self
.
url
,
{
"name"
:
"file.txt"
},
"application/json"
)
self
.
assertEquals
(
resp
.
status_code
,
400
)
self
.
assertEquals
(
resp
.
status_code
,
400
)
@data
(
(
int
(
MAX_FILE_SIZE
/
2.0
),
"small.file.test"
,
200
),
(
MAX_FILE_SIZE
,
"justequals.file.test"
,
200
),
(
MAX_FILE_SIZE
+
90
,
"large.file.test"
,
413
),
)
@mock.patch
(
'contentstore.views.assets.get_file_size'
)
def
test_file_size
(
self
,
case
,
get_file_size
):
max_file_size
,
name
,
status_code
=
case
get_file_size
.
return_value
=
max_file_size
f
=
self
.
get_sample_asset
(
name
=
name
)
resp
=
self
.
client
.
post
(
self
.
url
,
{
"name"
:
name
,
"file"
:
f
})
self
.
assertEquals
(
resp
.
status_code
,
status_code
)
class
DownloadTestCase
(
AssetsTestCase
):
class
DownloadTestCase
(
AssetsTestCase
):
"""
"""
...
...
cms/envs/common.py
View file @
5c433ec9
...
@@ -718,6 +718,16 @@ ADVANCED_SECURITY_CONFIG = {}
...
@@ -718,6 +718,16 @@ ADVANCED_SECURITY_CONFIG = {}
SHIBBOLETH_DOMAIN_PREFIX
=
'shib:'
SHIBBOLETH_DOMAIN_PREFIX
=
'shib:'
OPENID_DOMAIN_PREFIX
=
'openid:'
OPENID_DOMAIN_PREFIX
=
'openid:'
### Size of chunks into which asset uploads will be divided
UPLOAD_CHUNK_SIZE_IN_MB
=
10
### Max size of asset uploads to GridFS
MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB
=
10
# FAQ url to direct users to if they upload
# a file that exceeds the above size
MAX_ASSET_UPLOAD_FILE_SIZE_URL
=
""
################ ADVANCED_COMPONENT_TYPES ###############
################ ADVANCED_COMPONENT_TYPES ###############
ADVANCED_COMPONENT_TYPES
=
[
ADVANCED_COMPONENT_TYPES
=
[
...
...
cms/static/coffee/spec/main.coffee
View file @
5c433ec9
...
@@ -15,6 +15,8 @@ requirejs.config({
...
@@ -15,6 +15,8 @@ requirejs.config({
"jquery.cookie"
:
"xmodule_js/common_static/js/vendor/jquery.cookie"
,
"jquery.cookie"
:
"xmodule_js/common_static/js/vendor/jquery.cookie"
,
"jquery.qtip"
:
"xmodule_js/common_static/js/vendor/jquery.qtip.min"
,
"jquery.qtip"
:
"xmodule_js/common_static/js/vendor/jquery.qtip.min"
,
"jquery.fileupload"
:
"xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload"
,
"jquery.fileupload"
:
"xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload"
,
"jquery.fileupload-process"
:
"xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload-process"
,
"jquery.fileupload-validate"
:
"xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload-validate"
,
"jquery.iframe-transport"
:
"xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.iframe-transport"
,
"jquery.iframe-transport"
:
"xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.iframe-transport"
,
"jquery.inputnumber"
:
"xmodule_js/common_static/js/vendor/html5-input-polyfills/number-polyfill"
,
"jquery.inputnumber"
:
"xmodule_js/common_static/js/vendor/html5-input-polyfills/number-polyfill"
,
"jquery.immediateDescendents"
:
"xmodule_js/common_static/coffee/src/jquery.immediateDescendents"
,
"jquery.immediateDescendents"
:
"xmodule_js/common_static/coffee/src/jquery.immediateDescendents"
,
...
@@ -94,9 +96,15 @@ requirejs.config({
...
@@ -94,9 +96,15 @@ requirejs.config({
exports
:
"jQuery.fn.qtip"
exports
:
"jQuery.fn.qtip"
},
},
"jquery.fileupload"
:
{
"jquery.fileupload"
:
{
deps
:
[
"jquery.iframe-transport"
],
deps
:
[
"jquery.
ui"
,
"jquery.
iframe-transport"
],
exports
:
"jQuery.fn.fileupload"
exports
:
"jQuery.fn.fileupload"
},
},
"jquery.fileupload-process"
:
{
deps
:
[
"jquery.fileupload"
]
},
"jquery.fileupload-validate"
:
{
deps
:
[
"jquery.fileupload"
]
},
"jquery.inputnumber"
:
{
"jquery.inputnumber"
:
{
deps
:
[
"jquery"
],
deps
:
[
"jquery"
],
exports
:
"jQuery.fn.inputNumber"
exports
:
"jQuery.fn.inputNumber"
...
...
cms/static/coffee/spec/main_squire.coffee
View file @
5c433ec9
...
@@ -14,6 +14,8 @@ requirejs.config({
...
@@ -14,6 +14,8 @@ requirejs.config({
"jquery.cookie"
:
"xmodule_js/common_static/js/vendor/jquery.cookie"
,
"jquery.cookie"
:
"xmodule_js/common_static/js/vendor/jquery.cookie"
,
"jquery.qtip"
:
"xmodule_js/common_static/js/vendor/jquery.qtip.min"
,
"jquery.qtip"
:
"xmodule_js/common_static/js/vendor/jquery.qtip.min"
,
"jquery.fileupload"
:
"xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload"
,
"jquery.fileupload"
:
"xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload"
,
"jquery.fileupload-process"
:
"xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload-process"
,
"jquery.fileupload-validate"
:
"xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload-validate"
,
"jquery.iframe-transport"
:
"xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.iframe-transport"
,
"jquery.iframe-transport"
:
"xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.iframe-transport"
,
"jquery.inputnumber"
:
"xmodule_js/common_static/js/vendor/html5-input-polyfills/number-polyfill"
,
"jquery.inputnumber"
:
"xmodule_js/common_static/js/vendor/html5-input-polyfills/number-polyfill"
,
"jquery.immediateDescendents"
:
"xmodule_js/common_static/coffee/src/jquery.immediateDescendents"
,
"jquery.immediateDescendents"
:
"xmodule_js/common_static/coffee/src/jquery.immediateDescendents"
,
...
@@ -84,9 +86,15 @@ requirejs.config({
...
@@ -84,9 +86,15 @@ requirejs.config({
exports
:
"jQuery.fn.qtip"
exports
:
"jQuery.fn.qtip"
},
},
"jquery.fileupload"
:
{
"jquery.fileupload"
:
{
deps
:
[
"jquery.iframe-transport"
],
deps
:
[
"jquery.
ui"
,
"jquery.
iframe-transport"
],
exports
:
"jQuery.fn.fileupload"
exports
:
"jQuery.fn.fileupload"
},
},
"jquery.fileupload-process"
:
{
deps
:
[
"jquery.fileupload"
]
},
"jquery.fileupload-validate"
:
{
deps
:
[
"jquery.fileupload"
]
},
"jquery.inputnumber"
:
{
"jquery.inputnumber"
:
{
deps
:
[
"jquery"
],
deps
:
[
"jquery"
],
exports
:
"jQuery.fn.inputNumber"
exports
:
"jQuery.fn.inputNumber"
...
...
cms/static/coffee/spec/views/assets_spec.coffee
View file @
5c433ec9
...
@@ -48,9 +48,12 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
...
@@ -48,9 +48,12 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
@
collection
=
new
AssetCollection
([
@
model
])
@
collection
=
new
AssetCollection
([
@
model
])
@
collection
.
url
=
"assets-url"
@
collection
.
url
=
"assets-url"
@
view
=
new
AssetView
({
model
:
@
model
})
@
createAssetView
=
(
test
)
=>
view
=
new
AssetView
({
model
:
@
model
})
requests
=
if
test
then
AjaxHelpers
[
"requests"
](
test
)
else
null
return
{
view
:
view
,
requests
:
requests
}
waitsFor
(
=>
@
view
),
"AssetView was not creat
ed"
,
1000
waitsFor
(
=>
@
createAssetView
),
"AssetsView Creation function was not initializ
ed"
,
1000
afterEach
->
afterEach
->
@
injector
.
clean
()
@
injector
.
clean
()
...
@@ -58,10 +61,12 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
...
@@ -58,10 +61,12 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
describe
"Basic"
,
->
describe
"Basic"
,
->
it
"should render properly"
,
->
it
"should render properly"
,
->
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetView
()
@
view
.
render
()
@
view
.
render
()
expect
(
@
view
.
$el
).
toContainText
(
"test asset"
)
expect
(
@
view
.
$el
).
toContainText
(
"test asset"
)
it
"should pop a delete confirmation when the delete button is clicked"
,
->
it
"should pop a delete confirmation when the delete button is clicked"
,
->
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetView
()
@
view
.
render
().
$
(
".remove-asset-button"
).
click
()
@
view
.
render
().
$
(
".remove-asset-button"
).
click
()
expect
(
@
promptSpies
.
constructor
).
toHaveBeenCalled
()
expect
(
@
promptSpies
.
constructor
).
toHaveBeenCalled
()
ctorOptions
=
@
promptSpies
.
constructor
.
mostRecentCall
.
args
[
0
]
ctorOptions
=
@
promptSpies
.
constructor
.
mostRecentCall
.
args
[
0
]
...
@@ -72,7 +77,7 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
...
@@ -72,7 +77,7 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
describe
"AJAX"
,
->
describe
"AJAX"
,
->
it
"should destroy itself on confirmation"
,
->
it
"should destroy itself on confirmation"
,
->
requests
=
AjaxHelpers
[
"requests"
]
(
this
)
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetView
(
this
)
@
view
.
render
().
$
(
".remove-asset-button"
).
click
()
@
view
.
render
().
$
(
".remove-asset-button"
).
click
()
ctorOptions
=
@
promptSpies
.
constructor
.
mostRecentCall
.
args
[
0
]
ctorOptions
=
@
promptSpies
.
constructor
.
mostRecentCall
.
args
[
0
]
...
@@ -92,7 +97,7 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
...
@@ -92,7 +97,7 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
expect
(
@
collection
.
contains
(
@
model
)).
toBeFalsy
()
expect
(
@
collection
.
contains
(
@
model
)).
toBeFalsy
()
it
"should not destroy itself if server errors"
,
->
it
"should not destroy itself if server errors"
,
->
requests
=
AjaxHelpers
[
"requests"
]
(
this
)
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetView
(
this
)
@
view
.
render
().
$
(
".remove-asset-button"
).
click
()
@
view
.
render
().
$
(
".remove-asset-button"
).
click
()
ctorOptions
=
@
promptSpies
.
constructor
.
mostRecentCall
.
args
[
0
]
ctorOptions
=
@
promptSpies
.
constructor
.
mostRecentCall
.
args
[
0
]
...
@@ -106,7 +111,7 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
...
@@ -106,7 +111,7 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
expect
(
@
collection
.
contains
(
@
model
)).
toBeTruthy
()
expect
(
@
collection
.
contains
(
@
model
)).
toBeTruthy
()
it
"should lock the asset on confirmation"
,
->
it
"should lock the asset on confirmation"
,
->
requests
=
AjaxHelpers
[
"requests"
]
(
this
)
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetView
(
this
)
@
view
.
render
().
$
(
".lock-checkbox"
).
click
()
@
view
.
render
().
$
(
".lock-checkbox"
).
click
()
# AJAX request has been sent, but not yet returned
# AJAX request has been sent, but not yet returned
...
@@ -123,7 +128,7 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
...
@@ -123,7 +128,7 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
expect
(
@
model
.
get
(
"locked"
)).
toBeTruthy
()
expect
(
@
model
.
get
(
"locked"
)).
toBeTruthy
()
it
"should not lock the asset if server errors"
,
->
it
"should not lock the asset if server errors"
,
->
requests
=
AjaxHelpers
[
"requests"
]
(
this
)
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetView
(
this
)
@
view
.
render
().
$
(
".lock-checkbox"
).
click
()
@
view
.
render
().
$
(
".lock-checkbox"
).
click
()
# return an error response
# return an error response
...
@@ -138,6 +143,7 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
...
@@ -138,6 +143,7 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
appendSetFixtures
(
$
(
"<script>"
,
{
id
:
"asset-tpl"
,
type
:
"text/template"
}).
text
(
assetTpl
))
appendSetFixtures
(
$
(
"<script>"
,
{
id
:
"asset-tpl"
,
type
:
"text/template"
}).
text
(
assetTpl
))
appendSetFixtures
(
$
(
"<script>"
,
{
id
:
"paging-header-tpl"
,
type
:
"text/template"
}).
text
(
pagingHeaderTpl
))
appendSetFixtures
(
$
(
"<script>"
,
{
id
:
"paging-header-tpl"
,
type
:
"text/template"
}).
text
(
pagingHeaderTpl
))
appendSetFixtures
(
$
(
"<script>"
,
{
id
:
"paging-footer-tpl"
,
type
:
"text/template"
}).
text
(
pagingFooterTpl
))
appendSetFixtures
(
$
(
"<script>"
,
{
id
:
"paging-footer-tpl"
,
type
:
"text/template"
}).
text
(
pagingFooterTpl
))
appendSetFixtures
(
$
(
"<script>"
,
{
id
:
"system-feedback-tpl"
,
type
:
"text/template"
}).
text
(
feedbackTpl
))
window
.
analytics
=
jasmine
.
createSpyObj
(
'analytics'
,
[
'track'
])
window
.
analytics
=
jasmine
.
createSpyObj
(
'analytics'
,
[
'track'
])
window
.
course_location_analytics
=
jasmine
.
createSpy
()
window
.
course_location_analytics
=
jasmine
.
createSpy
()
appendSetFixtures
(
sandbox
({
id
:
"asset_table_body"
}))
appendSetFixtures
(
sandbox
({
id
:
"asset_table_body"
}))
...
@@ -182,12 +188,16 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
...
@@ -182,12 +188,16 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
@
AssetModel
=
AssetModel
@
AssetModel
=
AssetModel
@
collection
=
new
AssetCollection
();
@
collection
=
new
AssetCollection
();
@
collection
.
url
=
"assets-url"
@
collection
.
url
=
"assets-url"
@
view
=
new
AssetsView
@
createAssetsView
=
(
test
)
=>
collection
:
@
collection
requests
=
AjaxHelpers
.
requests
(
test
)
el
:
$
(
'#asset_table_body'
)
view
=
new
AssetsView
@
view
.
render
()
collection
:
@
collection
el
:
$
(
'#asset_table_body'
)
view
.
render
()
return
{
view
:
view
,
requests
:
requests
}
waitsFor
(
=>
@
view
),
"AssetsView was not created"
,
1
000
waitsFor
(
=>
@
createAssetsView
),
"AssetsView Creation function was not initialized"
,
2
000
$
.
ajax
()
$
.
ajax
()
...
@@ -230,11 +240,9 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
...
@@ -230,11 +240,9 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
describe
"Basic"
,
->
describe
"Basic"
,
->
# Separate setup method to work-around mis-parenting of beforeEach methods
# Separate setup method to work-around mis-parenting of beforeEach methods
setup
=
->
setup
=
(
requests
)
->
requests
=
AjaxHelpers
.
requests
(
this
)
@
view
.
setPage
(
0
)
@
view
.
setPage
(
0
)
AjaxHelpers
.
respondWithJson
(
requests
,
@
mockAssetsResponse
)
AjaxHelpers
.
respondWithJson
(
requests
,
@
mockAssetsResponse
)
return
requests
$
.
fn
.
fileupload
=
->
$
.
fn
.
fileupload
=
->
return
''
return
''
...
@@ -243,34 +251,38 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
...
@@ -243,34 +251,38 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
$
(
html_selector
).
click
()
$
(
html_selector
).
click
()
it
"should show upload modal on clicking upload asset button"
,
->
it
"should show upload modal on clicking upload asset button"
,
->
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetsView
(
this
)
spyOn
(
@
view
,
"showUploadModal"
)
spyOn
(
@
view
,
"showUploadModal"
)
setup
.
call
(
this
)
setup
.
call
(
this
,
requests
)
expect
(
@
view
.
showUploadModal
).
not
.
toHaveBeenCalled
()
expect
(
@
view
.
showUploadModal
).
not
.
toHaveBeenCalled
()
@
view
.
showUploadModal
(
clickEvent
(
".upload-button"
))
@
view
.
showUploadModal
(
clickEvent
(
".upload-button"
))
expect
(
@
view
.
showUploadModal
).
toHaveBeenCalled
()
expect
(
@
view
.
showUploadModal
).
toHaveBeenCalled
()
it
"should show file selection menu on choose file button"
,
->
it
"should show file selection menu on choose file button"
,
->
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetsView
(
this
)
spyOn
(
@
view
,
"showFileSelectionMenu"
)
spyOn
(
@
view
,
"showFileSelectionMenu"
)
setup
.
call
(
this
)
setup
.
call
(
this
,
requests
)
expect
(
@
view
.
showFileSelectionMenu
).
not
.
toHaveBeenCalled
()
expect
(
@
view
.
showFileSelectionMenu
).
not
.
toHaveBeenCalled
()
@
view
.
showFileSelectionMenu
(
clickEvent
(
".choose-file-button"
))
@
view
.
showFileSelectionMenu
(
clickEvent
(
".choose-file-button"
))
expect
(
@
view
.
showFileSelectionMenu
).
toHaveBeenCalled
()
expect
(
@
view
.
showFileSelectionMenu
).
toHaveBeenCalled
()
it
"should hide upload modal on clicking close button"
,
->
it
"should hide upload modal on clicking close button"
,
->
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetsView
(
this
)
spyOn
(
@
view
,
"hideModal"
)
spyOn
(
@
view
,
"hideModal"
)
setup
.
call
(
this
)
setup
.
call
(
this
,
requests
)
expect
(
@
view
.
hideModal
).
not
.
toHaveBeenCalled
()
expect
(
@
view
.
hideModal
).
not
.
toHaveBeenCalled
()
@
view
.
hideModal
(
clickEvent
(
".close-button"
))
@
view
.
hideModal
(
clickEvent
(
".close-button"
))
expect
(
@
view
.
hideModal
).
toHaveBeenCalled
()
expect
(
@
view
.
hideModal
).
toHaveBeenCalled
()
it
"should show a status indicator while loading"
,
->
it
"should show a status indicator while loading"
,
->
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetsView
(
this
)
appendSetFixtures
(
'<div class="ui-loading"/>'
)
appendSetFixtures
(
'<div class="ui-loading"/>'
)
expect
(
$
(
'.ui-loading'
).
is
(
':visible'
)).
toBe
(
true
)
expect
(
$
(
'.ui-loading'
).
is
(
':visible'
)).
toBe
(
true
)
setup
.
call
(
this
)
setup
.
call
(
this
,
requests
)
expect
(
$
(
'.ui-loading'
).
is
(
':visible'
)).
toBe
(
false
)
expect
(
$
(
'.ui-loading'
).
is
(
':visible'
)).
toBe
(
false
)
it
"should hide the status indicator if an error occurs while loading"
,
->
it
"should hide the status indicator if an error occurs while loading"
,
->
requests
=
AjaxHelpers
.
requests
(
this
)
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetsView
(
this
)
appendSetFixtures
(
'<div class="ui-loading"/>'
)
appendSetFixtures
(
'<div class="ui-loading"/>'
)
expect
(
$
(
'.ui-loading'
).
is
(
':visible'
)).
toBe
(
true
)
expect
(
$
(
'.ui-loading'
).
is
(
':visible'
)).
toBe
(
true
)
@
view
.
setPage
(
0
)
@
view
.
setPage
(
0
)
...
@@ -278,21 +290,24 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
...
@@ -278,21 +290,24 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
expect
(
$
(
'.ui-loading'
).
is
(
':visible'
)).
toBe
(
false
)
expect
(
$
(
'.ui-loading'
).
is
(
':visible'
)).
toBe
(
false
)
it
"should render both assets"
,
->
it
"should render both assets"
,
->
requests
=
setup
.
call
(
this
)
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetsView
(
this
)
setup
.
call
(
this
,
requests
)
expect
(
@
view
.
$el
).
toContainText
(
"test asset 1"
)
expect
(
@
view
.
$el
).
toContainText
(
"test asset 1"
)
expect
(
@
view
.
$el
).
toContainText
(
"test asset 2"
)
expect
(
@
view
.
$el
).
toContainText
(
"test asset 2"
)
it
"should remove the deleted asset from the view"
,
->
it
"should remove the deleted asset from the view"
,
->
requests
=
setup
.
call
(
this
)
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetsView
(
this
)
setup
.
call
(
this
,
requests
)
# Delete the 2nd asset with success from server.
# Delete the 2nd asset with success from server.
@
view
.
$
(
".remove-asset-button"
)[
1
].
click
()
@
view
.
$
(
".remove-asset-button"
)[
1
].
click
()
@
promptSpies
.
constructor
.
mostRecentCall
.
args
[
0
].
actions
.
primary
.
click
(
@
promptSpies
)
@
promptSpies
.
constructor
.
mostRecentCall
.
args
[
0
].
actions
.
primary
.
click
(
@
promptSpies
)
req
.
respond
(
200
)
for
req
in
requests
req
.
respond
(
200
)
for
req
in
requests
.
slice
(
1
)
expect
(
@
view
.
$el
).
toContainText
(
"test asset 1"
)
expect
(
@
view
.
$el
).
toContainText
(
"test asset 1"
)
expect
(
@
view
.
$el
).
not
.
toContainText
(
"test asset 2"
)
expect
(
@
view
.
$el
).
not
.
toContainText
(
"test asset 2"
)
it
"does not remove asset if deletion failed"
,
->
it
"does not remove asset if deletion failed"
,
->
requests
=
setup
.
call
(
this
)
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetsView
(
this
)
setup
.
call
(
this
,
requests
)
# Delete the 2nd asset, but mimic a failure from the server.
# Delete the 2nd asset, but mimic a failure from the server.
@
view
.
$
(
".remove-asset-button"
)[
1
].
click
()
@
view
.
$
(
".remove-asset-button"
)[
1
].
click
()
@
promptSpies
.
constructor
.
mostRecentCall
.
args
[
0
].
actions
.
primary
.
click
(
@
promptSpies
)
@
promptSpies
.
constructor
.
mostRecentCall
.
args
[
0
].
actions
.
primary
.
click
(
@
promptSpies
)
...
@@ -301,13 +316,15 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
...
@@ -301,13 +316,15 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
expect
(
@
view
.
$el
).
toContainText
(
"test asset 2"
)
expect
(
@
view
.
$el
).
toContainText
(
"test asset 2"
)
it
"adds an asset if asset does not already exist"
,
->
it
"adds an asset if asset does not already exist"
,
->
requests
=
setup
.
call
(
this
)
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetsView
(
this
)
setup
.
call
(
this
,
requests
)
addMockAsset
.
call
(
this
,
requests
)
addMockAsset
.
call
(
this
,
requests
)
expect
(
@
view
.
$el
).
toContainText
(
"new asset"
)
expect
(
@
view
.
$el
).
toContainText
(
"new asset"
)
expect
(
@
collection
.
models
.
length
).
toBe
(
3
)
expect
(
@
collection
.
models
.
length
).
toBe
(
3
)
it
"does not add an asset if asset already exists"
,
->
it
"does not add an asset if asset already exists"
,
->
setup
.
call
(
this
)
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetsView
(
this
)
setup
.
call
(
this
,
requests
)
spyOn
(
@
collection
,
"add"
).
andCallThrough
()
spyOn
(
@
collection
,
"add"
).
andCallThrough
()
model
=
@
collection
.
models
[
1
]
model
=
@
collection
.
models
[
1
]
@
view
.
addAsset
(
model
)
@
view
.
addAsset
(
model
)
...
@@ -315,19 +332,19 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
...
@@ -315,19 +332,19 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
describe
"Sorting"
,
->
describe
"Sorting"
,
->
# Separate setup method to work-around mis-parenting of beforeEach methods
# Separate setup method to work-around mis-parenting of beforeEach methods
setup
=
->
setup
=
(
requests
)
->
requests
=
AjaxHelpers
.
requests
(
this
)
@
view
.
setPage
(
0
)
@
view
.
setPage
(
0
)
AjaxHelpers
.
respondWithJson
(
requests
,
@
mockAssetsResponse
)
AjaxHelpers
.
respondWithJson
(
requests
,
@
mockAssetsResponse
)
return
requests
it
"should have the correct default sort order"
,
->
it
"should have the correct default sort order"
,
->
requests
=
setup
.
call
(
this
)
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetsView
(
this
)
setup
.
call
(
this
,
requests
)
expect
(
@
view
.
sortDisplayName
()).
toBe
(
"Date Added"
)
expect
(
@
view
.
sortDisplayName
()).
toBe
(
"Date Added"
)
expect
(
@
view
.
collection
.
sortDirection
).
toBe
(
"desc"
)
expect
(
@
view
.
collection
.
sortDirection
).
toBe
(
"desc"
)
it
"should toggle the sort order when clicking on the currently sorted column"
,
->
it
"should toggle the sort order when clicking on the currently sorted column"
,
->
requests
=
setup
.
call
(
this
)
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetsView
(
this
)
setup
.
call
(
this
,
requests
)
expect
(
@
view
.
sortDisplayName
()).
toBe
(
"Date Added"
)
expect
(
@
view
.
sortDisplayName
()).
toBe
(
"Date Added"
)
expect
(
@
view
.
collection
.
sortDirection
).
toBe
(
"desc"
)
expect
(
@
view
.
collection
.
sortDirection
).
toBe
(
"desc"
)
@
view
.
$
(
"#js-asset-date-col"
).
click
()
@
view
.
$
(
"#js-asset-date-col"
).
click
()
...
@@ -340,7 +357,8 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
...
@@ -340,7 +357,8 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
expect
(
@
view
.
collection
.
sortDirection
).
toBe
(
"desc"
)
expect
(
@
view
.
collection
.
sortDirection
).
toBe
(
"desc"
)
it
"should switch the sort order when clicking on a different column"
,
->
it
"should switch the sort order when clicking on a different column"
,
->
requests
=
setup
.
call
(
this
)
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetsView
(
this
)
setup
.
call
(
this
,
requests
)
@
view
.
$
(
"#js-asset-name-col"
).
click
()
@
view
.
$
(
"#js-asset-name-col"
).
click
()
AjaxHelpers
.
respondWithJson
(
requests
,
@
mockAssetsResponse
)
AjaxHelpers
.
respondWithJson
(
requests
,
@
mockAssetsResponse
)
expect
(
@
view
.
sortDisplayName
()).
toBe
(
"Name"
)
expect
(
@
view
.
sortDisplayName
()).
toBe
(
"Name"
)
...
@@ -351,7 +369,8 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
...
@@ -351,7 +369,8 @@ define ["jquery", "jasmine", "js/common_helpers/ajax_helpers", "squire"],
expect
(
@
view
.
collection
.
sortDirection
).
toBe
(
"desc"
)
expect
(
@
view
.
collection
.
sortDirection
).
toBe
(
"desc"
)
it
"should switch sort to most recent date added when a new asset is added"
,
->
it
"should switch sort to most recent date added when a new asset is added"
,
->
requests
=
setup
.
call
(
this
)
{
view
:
@
view
,
requests
:
requests
}
=
@
createAssetsView
(
this
)
setup
.
call
(
this
,
requests
)
@
view
.
$
(
"#js-asset-name-col"
).
click
()
@
view
.
$
(
"#js-asset-name-col"
).
click
()
AjaxHelpers
.
respondWithJson
(
requests
,
@
mockAssetsResponse
)
AjaxHelpers
.
respondWithJson
(
requests
,
@
mockAssetsResponse
)
addMockAsset
.
call
(
this
,
requests
)
addMockAsset
.
call
(
this
,
requests
)
...
...
cms/static/js/factories/asset_index.js
View file @
5c433ec9
...
@@ -2,12 +2,18 @@ define([
...
@@ -2,12 +2,18 @@ define([
'jquery'
,
'js/collections/asset'
,
'js/views/assets'
,
'jquery.fileupload'
'jquery'
,
'js/collections/asset'
,
'js/views/assets'
,
'jquery.fileupload'
],
function
(
$
,
AssetCollection
,
AssetsView
)
{
],
function
(
$
,
AssetCollection
,
AssetsView
)
{
'use strict'
;
'use strict'
;
return
function
(
assetCallbackUrl
)
{
return
function
(
config
)
{
var
assets
=
new
AssetCollection
(),
var
assets
=
new
AssetCollection
(),
assetsView
;
assetsView
;
assets
.
url
=
assetCallbackUrl
;
assets
.
url
=
config
.
assetCallbackUrl
;
assetsView
=
new
AssetsView
({
collection
:
assets
,
el
:
$
(
'.assets-wrapper'
)});
assetsView
=
new
AssetsView
({
collection
:
assets
,
el
:
$
(
'.assets-wrapper'
),
uploadChunkSizeInMBs
:
config
.
uploadChunkSizeInMBs
,
maxFileSizeInMBs
:
config
.
maxFileSizeInMBs
,
maxFileSizeRedirectUrl
:
config
.
maxFileSizeRedirectUrl
});
assetsView
.
render
();
assetsView
.
render
();
};
};
});
});
cms/static/js/spec/views/assets_spec.js
View file @
5c433ec9
define
([
"jquery"
,
"js/common_helpers/ajax_helpers"
,
"js/views/asset"
,
"js/views/assets"
,
define
([
"jquery"
,
"js/common_helpers/ajax_helpers"
,
"js/views/asset"
,
"js/views/assets"
,
"js/models/asset"
,
"js/collections/asset"
,
"js/spec_helpers/view_helpers"
],
"js/models/asset"
,
"js/collections/asset"
,
"js/spec_helpers/view_helpers"
],
function
(
$
,
AjaxHelpers
,
AssetView
,
AssetsView
,
AssetModel
,
AssetCollection
,
ViewHelpers
)
{
function
(
$
,
AjaxHelpers
,
AssetView
,
AssetsView
,
AssetModel
,
AssetCollection
,
ViewHelpers
)
{
describe
(
"Assets"
,
function
()
{
describe
(
"Assets"
,
function
()
{
var
assetsView
,
mockEmptyAssetsResponse
,
mockAssetUploadResponse
,
var
assetsView
,
mockEmptyAssetsResponse
,
mockAssetUploadResponse
,
mockFileUpload
,
assetLibraryTpl
,
assetTpl
,
pagingFooterTpl
,
pagingHeaderTpl
,
uploadModalTpl
;
assetLibraryTpl
,
assetTpl
,
pagingFooterTpl
,
pagingHeaderTpl
,
uploadModalTpl
;
assetLibraryTpl
=
readFixtures
(
'asset-library.underscore'
);
assetLibraryTpl
=
readFixtures
(
'asset-library.underscore'
);
...
@@ -53,6 +53,10 @@ define([ "jquery", "js/common_helpers/ajax_helpers", "js/views/asset", "js/views
...
@@ -53,6 +53,10 @@ define([ "jquery", "js/common_helpers/ajax_helpers", "js/views/asset", "js/views
msg
:
"Upload completed"
msg
:
"Upload completed"
};
};
mockFileUpload
=
{
files
:
[{
name
:
'largefile'
,
size
:
0
}]
};
$
.
fn
.
fileupload
=
function
()
{
$
.
fn
.
fileupload
=
function
()
{
return
''
;
return
''
;
};
};
...
@@ -95,6 +99,15 @@ define([ "jquery", "js/common_helpers/ajax_helpers", "js/views/asset", "js/views
...
@@ -95,6 +99,15 @@ define([ "jquery", "js/common_helpers/ajax_helpers", "js/views/asset", "js/views
expect
(
$
(
'.upload-modal'
).
is
(
':visible'
)).
toBe
(
false
);
expect
(
$
(
'.upload-modal'
).
is
(
':visible'
)).
toBe
(
false
);
});
});
it
(
'has properly initialized constants for handling upload file errors'
,
function
()
{
expect
(
assetsView
).
toBeDefined
();
expect
(
assetsView
.
uploadChunkSizeInMBs
).
toBeDefined
();
expect
(
assetsView
.
maxFileSizeInMBs
).
toBeDefined
();
expect
(
assetsView
.
uploadChunkSizeInBytes
).
toBeDefined
();
expect
(
assetsView
.
maxFileSizeInBytes
).
toBeDefined
();
expect
(
assetsView
.
largeFileErrorMsg
).
toBeNull
();
});
it
(
'uploads file properly'
,
function
()
{
it
(
'uploads file properly'
,
function
()
{
var
requests
=
setup
.
call
(
this
);
var
requests
=
setup
.
call
(
this
);
expect
(
assetsView
).
toBeDefined
();
expect
(
assetsView
).
toBeDefined
();
...
@@ -122,6 +135,42 @@ define([ "jquery", "js/common_helpers/ajax_helpers", "js/views/asset", "js/views
...
@@ -122,6 +135,42 @@ define([ "jquery", "js/common_helpers/ajax_helpers", "js/views/asset", "js/views
expect
(
$
(
'#asset_table_body'
).
html
()).
toContain
(
"dummy.jpg"
);
expect
(
$
(
'#asset_table_body'
).
html
()).
toContain
(
"dummy.jpg"
);
expect
(
assetsView
.
collection
.
length
).
toBe
(
1
);
expect
(
assetsView
.
collection
.
length
).
toBe
(
1
);
});
});
it
(
'blocks file uploads larger than the max file size'
,
function
()
{
expect
(
assetsView
).
toBeDefined
();
mockFileUpload
.
files
[
0
].
size
=
assetsView
.
maxFileSize
*
10
;
$
(
'.choose-file-button'
).
click
();
$
(
".upload-modal .file-chooser"
).
fileupload
(
'add'
,
mockFileUpload
);
expect
(
$
(
'.upload-modal h1'
).
text
()).
not
.
toContain
(
"Uploading"
);
expect
(
assetsView
.
largeFileErrorMsg
).
toBeDefined
();
expect
(
$
(
'div.progress-bar'
).
text
()).
not
.
toContain
(
"Upload completed"
);
expect
(
$
(
'div.progress-fill'
).
width
()).
toBe
(
0
);
});
it
(
'allows file uploads equal in size to the max file size'
,
function
()
{
expect
(
assetsView
).
toBeDefined
();
mockFileUpload
.
files
[
0
].
size
=
assetsView
.
maxFileSize
;
$
(
'.choose-file-button'
).
click
();
$
(
".upload-modal .file-chooser"
).
fileupload
(
'add'
,
mockFileUpload
);
expect
(
assetsView
.
largeFileErrorMsg
).
toBeNull
();
});
it
(
'allows file uploads smaller than the max file size'
,
function
()
{
expect
(
assetsView
).
toBeDefined
();
mockFileUpload
.
files
[
0
].
size
=
assetsView
.
maxFileSize
/
100
;
$
(
'.choose-file-button'
).
click
();
$
(
".upload-modal .file-chooser"
).
fileupload
(
'add'
,
mockFileUpload
);
expect
(
assetsView
.
largeFileErrorMsg
).
toBeNull
();
});
});
});
});
});
});
});
cms/static/js/views/assets.js
View file @
5c433ec9
define
([
"jquery"
,
"underscore"
,
"gettext"
,
"js/models/asset"
,
"js/views/paging"
,
"js/views/asset"
,
define
([
"jquery"
,
"underscore"
,
"gettext"
,
"js/models/asset"
,
"js/views/paging"
,
"js/views/asset"
,
"js/views/paging_header"
,
"js/views/paging_footer"
,
"js/utils/modal"
,
"js/views/utils/view_utils"
],
"js/views/paging_header"
,
"js/views/paging_footer"
,
"js/utils/modal"
,
"js/views/utils/view_utils"
,
function
(
$
,
_
,
gettext
,
AssetModel
,
PagingView
,
AssetView
,
PagingHeader
,
PagingFooter
,
ModalUtils
,
ViewUtils
)
{
"js/views/feedback_notification"
,
"jquery.fileupload-process"
,
"jquery.fileupload-validate"
],
function
(
$
,
_
,
gettext
,
AssetModel
,
PagingView
,
AssetView
,
PagingHeader
,
PagingFooter
,
ModalUtils
,
ViewUtils
,
NotificationView
)
{
var
CONVERSION_FACTOR_MBS_TO_BYTES
=
1000
*
1000
;
var
AssetsView
=
PagingView
.
extend
({
var
AssetsView
=
PagingView
.
extend
({
// takes AssetCollection as model
// takes AssetCollection as model
...
@@ -10,7 +13,9 @@ define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging",
...
@@ -10,7 +13,9 @@ define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging",
"click .upload-button"
:
"showUploadModal"
"click .upload-button"
:
"showUploadModal"
},
},
initialize
:
function
()
{
initialize
:
function
(
options
)
{
options
=
options
||
{};
PagingView
.
prototype
.
initialize
.
call
(
this
);
PagingView
.
prototype
.
initialize
.
call
(
this
);
var
collection
=
this
.
collection
;
var
collection
=
this
.
collection
;
this
.
template
=
this
.
loadTemplate
(
"asset-library"
);
this
.
template
=
this
.
loadTemplate
(
"asset-library"
);
...
@@ -20,7 +25,16 @@ define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging",
...
@@ -20,7 +25,16 @@ define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging",
this
.
setInitialSortColumn
(
'js-asset-date-col'
);
this
.
setInitialSortColumn
(
'js-asset-date-col'
);
ViewUtils
.
showLoadingIndicator
();
ViewUtils
.
showLoadingIndicator
();
this
.
setPage
(
0
);
this
.
setPage
(
0
);
// set default file size for uploads via template var,
// and default to static old value if none exists
this
.
uploadChunkSizeInMBs
=
options
.
uploadChunkSizeInMBs
||
10
;
this
.
maxFileSizeInMBs
=
options
.
maxFileSizeInMBs
||
10
;
this
.
uploadChunkSizeInBytes
=
this
.
uploadChunkSizeInMBs
*
CONVERSION_FACTOR_MBS_TO_BYTES
;
this
.
maxFileSizeInBytes
=
this
.
maxFileSizeInMBs
*
CONVERSION_FACTOR_MBS_TO_BYTES
;
this
.
maxFileSizeRedirectUrl
=
options
.
maxFileSizeRedirectUrl
||
''
;
assetsView
=
this
;
assetsView
=
this
;
// error message modal for large file uploads
this
.
largeFileErrorMsg
=
null
;
},
},
render
:
function
()
{
render
:
function
()
{
...
@@ -111,6 +125,9 @@ define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging",
...
@@ -111,6 +125,9 @@ define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging",
}
}
$
(
'.file-input'
).
unbind
(
'change.startUpload'
);
$
(
'.file-input'
).
unbind
(
'change.startUpload'
);
ModalUtils
.
hideModal
();
ModalUtils
.
hideModal
();
if
(
assetsView
.
largeFileErrorMsg
)
{
assetsView
.
largeFileErrorMsg
.
hide
();
}
},
},
showUploadModal
:
function
(
event
)
{
showUploadModal
:
function
(
event
)
{
...
@@ -122,23 +139,44 @@ define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging",
...
@@ -122,23 +139,44 @@ define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging",
$
(
'.upload-modal .file-chooser'
).
fileupload
({
$
(
'.upload-modal .file-chooser'
).
fileupload
({
dataType
:
'json'
,
dataType
:
'json'
,
type
:
'POST'
,
type
:
'POST'
,
maxChunkSize
:
100
*
1000
*
1000
,
// 100 MB
maxChunkSize
:
self
.
uploadChunkSizeInBytes
,
autoUpload
:
true
,
autoUpload
:
true
,
progressall
:
function
(
event
,
data
)
{
progressall
:
function
(
event
,
data
)
{
var
percentComplete
=
parseInt
((
100
*
data
.
loaded
)
/
data
.
total
,
10
);
var
percentComplete
=
parseInt
((
100
*
data
.
loaded
)
/
data
.
total
,
10
);
self
.
showUploadFeedback
(
event
,
percentComplete
);
self
.
showUploadFeedback
(
event
,
percentComplete
);
},
},
maxFileSize
:
100
*
1000
*
1000
,
// 100 MB
maxFileSize
:
self
.
maxFileSizeInBytes
,
maxNumberofFiles
:
100
,
maxNumberofFiles
:
100
,
add
:
function
(
event
,
data
)
{
data
.
process
().
done
(
function
()
{
data
.
submit
();
});
},
done
:
function
(
event
,
data
)
{
done
:
function
(
event
,
data
)
{
self
.
displayFinishedUpload
(
data
.
result
);
self
.
displayFinishedUpload
(
data
.
result
);
}
},
processfail
:
function
(
event
,
data
)
{
var
filename
=
data
.
files
[
data
.
index
].
name
;
var
error
=
gettext
(
"File {filename} exceeds maximum size of {maxFileSizeInMBs} MB"
)
.
replace
(
"{filename}"
,
filename
)
.
replace
(
"{maxFileSizeInMBs}"
,
self
.
maxFileSizeInMBs
)
// disable second part of message for any falsy value,
// which can be null or an empty string
if
(
self
.
maxFileSizeRedirectUrl
)
{
var
instructions
=
gettext
(
"Please follow the instructions here to upload a file elsewhere and link to it: {maxFileSizeRedirectUrl}"
)
.
replace
(
"{maxFileSizeRedirectUrl}"
,
self
.
maxFileSizeRedirectUrl
);
error
=
error
+
" "
+
instructions
;
}
assetsView
.
largeFileErrorMsg
=
new
NotificationView
.
Error
({
"title"
:
gettext
(
"Your file could not be uploaded"
),
"message"
:
error
});
assetsView
.
largeFileErrorMsg
.
show
();
assetsView
.
displayFailedUpload
({
"msg"
:
gettext
(
"Max file size exceeded"
)
});
},
processdone
:
function
(
event
,
data
)
{
assetsView
.
largeFileErrorMsg
=
null
;
}
});
});
},
},
...
@@ -149,11 +187,12 @@ define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging",
...
@@ -149,11 +187,12 @@ define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging",
startUpload
:
function
(
event
)
{
startUpload
:
function
(
event
)
{
var
file
=
event
.
target
.
value
;
var
file
=
event
.
target
.
value
;
if
(
!
assetsView
.
largeFileErrorMsg
)
{
$
(
'.upload-modal h1'
).
text
(
gettext
(
'Uploading…'
));
$
(
'.upload-modal h1'
).
text
(
gettext
(
'Uploading'
));
$
(
'.upload-modal .file-name'
).
html
(
file
.
substring
(
file
.
lastIndexOf
(
"
\
\"
) + 1));
$
(
'.upload-modal .file-name'
).
html
(
file
.
substring
(
file
.
lastIndexOf
(
"
\
\"
) + 1));
$('.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();
}
},
},
resetUploadModal: function () {
resetUploadModal: function () {
...
@@ -169,6 +208,8 @@ define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging",
...
@@ -169,6 +208,8 @@ define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging",
$('.upload-modal .choose-file-button').text(gettext('Choose File'));
$('.upload-modal .choose-file-button').text(gettext('Choose File'));
$('.upload-modal .embeddable-xml-input').val('');
$('.upload-modal .embeddable-xml-input').val('');
$('.upload-modal .embeddable').hide();
$('.upload-modal .embeddable').hide();
assetsView.largeFileErrorMsg = null;
},
},
showUploadFeedback: function (event, percentComplete) {
showUploadFeedback: function (event, percentComplete) {
...
@@ -181,7 +222,7 @@ define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging",
...
@@ -181,7 +222,7 @@ define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging",
var asset = resp.asset;
var asset = resp.asset;
$('.upload-modal h1').text(gettext('Upload New File'));
$('.upload-modal h1').text(gettext('Upload New File'));
$('.upload-modal .embeddable-xml-input').val(asset.portable_url);
$('.upload-modal .embeddable-xml-input').val(asset.portable_url)
.show()
;
$('.upload-modal .embeddable').show();
$('.upload-modal .embeddable').show();
$('.upload-modal .file-name').hide();
$('.upload-modal .file-name').hide();
$('.upload-modal .progress-fill').html(resp.msg);
$('.upload-modal .progress-fill').html(resp.msg);
...
@@ -189,6 +230,16 @@ define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging",
...
@@ -189,6 +230,16 @@ define(["jquery", "underscore", "gettext", "js/models/asset", "js/views/paging",
$('.upload-modal .progress-fill').width('100%');
$('.upload-modal .progress-fill').width('100%');
assetsView.addAsset(new AssetModel(asset));
assetsView.addAsset(new AssetModel(asset));
},
displayFailedUpload: function (resp) {
$('.upload-modal h1').text(gettext('Upload New File'));
$('.upload-modal .embeddable-xml-input').hide();
$('.upload-modal .embeddable').hide();
$('.upload-modal .file-name').hide();
$('.upload-modal .progress-fill').html(resp.msg);
$('.upload-modal .choose-file-button').text(gettext('Load Another File')).show();
$('.upload-modal .progress-fill').width('0%');
}
}
});
});
...
...
cms/static/js_test.yml
View file @
5c433ec9
...
@@ -62,6 +62,10 @@ lib_paths:
...
@@ -62,6 +62,10 @@ lib_paths:
-
xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js
-
xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js
-
xmodule_js/common_static/coffee/src/xblock/
-
xmodule_js/common_static/coffee/src/xblock/
-
xmodule_js/common_static/js/vendor/URI.min.js
-
xmodule_js/common_static/js/vendor/URI.min.js
-
xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.iframe-transport.js
-
xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload.js
-
xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload-process.js
-
xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload-validate.js
# Paths to source JavaScript files
# Paths to source JavaScript files
src_paths
:
src_paths
:
...
...
cms/static/js_test_squire.yml
View file @
5c433ec9
...
@@ -57,6 +57,10 @@ lib_paths:
...
@@ -57,6 +57,10 @@ lib_paths:
-
xmodule_js/common_static/js/test/i18n.js
-
xmodule_js/common_static/js/test/i18n.js
-
xmodule_js/common_static/coffee/src/xblock/
-
xmodule_js/common_static/coffee/src/xblock/
-
xmodule_js/common_static/js/vendor/URI.min.js
-
xmodule_js/common_static/js/vendor/URI.min.js
-
xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.iframe-transport.js
-
xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload.js
-
xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload-process.js
-
xmodule_js/common_static/js/vendor/jQuery-File-Upload/js/jquery.fileupload-validate.js
# Paths to source JavaScript files
# Paths to source JavaScript files
src_paths
:
src_paths
:
...
...
cms/static/require-config.js
View file @
5c433ec9
...
@@ -20,6 +20,8 @@ require.config({
...
@@ -20,6 +20,8 @@ require.config({
"jquery.scrollTo"
:
"js/vendor/jquery.scrollTo-1.4.2-min"
,
"jquery.scrollTo"
:
"js/vendor/jquery.scrollTo-1.4.2-min"
,
"jquery.flot"
:
"js/vendor/flot/jquery.flot.min"
,
"jquery.flot"
:
"js/vendor/flot/jquery.flot.min"
,
"jquery.fileupload"
:
"js/vendor/jQuery-File-Upload/js/jquery.fileupload"
,
"jquery.fileupload"
:
"js/vendor/jQuery-File-Upload/js/jquery.fileupload"
,
"jquery.fileupload-process"
:
"js/vendor/jQuery-File-Upload/js/jquery.fileupload-process"
,
"jquery.fileupload-validate"
:
"js/vendor/jQuery-File-Upload/js/jquery.fileupload-validate"
,
"jquery.iframe-transport"
:
"js/vendor/jQuery-File-Upload/js/jquery.iframe-transport"
,
"jquery.iframe-transport"
:
"js/vendor/jQuery-File-Upload/js/jquery.iframe-transport"
,
"jquery.inputnumber"
:
"js/vendor/html5-input-polyfills/number-polyfill"
,
"jquery.inputnumber"
:
"js/vendor/html5-input-polyfills/number-polyfill"
,
"jquery.immediateDescendents"
:
"coffee/src/jquery.immediateDescendents"
,
"jquery.immediateDescendents"
:
"coffee/src/jquery.immediateDescendents"
,
...
@@ -128,9 +130,15 @@ require.config({
...
@@ -128,9 +130,15 @@ require.config({
exports
:
"jQuery.fn.plot"
exports
:
"jQuery.fn.plot"
},
},
"jquery.fileupload"
:
{
"jquery.fileupload"
:
{
deps
:
[
"jquery.iframe-transport"
],
deps
:
[
"jquery.
ui"
,
"jquery.
iframe-transport"
],
exports
:
"jQuery.fn.fileupload"
exports
:
"jQuery.fn.fileupload"
},
},
"jquery.fileupload-process"
:
{
deps
:
[
"jquery.fileupload"
]
},
"jquery.fileupload-validate"
:
{
deps
:
[
"jquery.fileupload"
]
},
"jquery.inputnumber"
:
{
"jquery.inputnumber"
:
{
deps
:
[
"jquery"
],
deps
:
[
"jquery"
],
exports
:
"jQuery.fn.inputNumber"
exports
:
"jQuery.fn.inputNumber"
...
...
cms/templates/asset_index.html
View file @
5c433ec9
...
@@ -19,7 +19,12 @@
...
@@ -19,7 +19,12 @@
<
%
block
name=
"requirejs"
>
<
%
block
name=
"requirejs"
>
require(["js/factories/asset_index"], function (AssetIndexFactory) {
require(["js/factories/asset_index"], function (AssetIndexFactory) {
AssetIndexFactory("${asset_callback_url}");
AssetIndexFactory({
assetCallbackUrl: "${asset_callback_url}",
uploadChunkSizeInMBs: ${chunk_size_in_mbs},
maxFileSizeInMBs: ${max_file_size_in_mbs},
maxFileSizeRedirectUrl: "${max_file_size_redirect_url}"
});
});
});
</
%
block>
</
%
block>
...
@@ -82,6 +87,7 @@
...
@@ -82,6 +87,7 @@
<a
href=
"#"
class=
"close-button"
><i
class=
"icon-remove-sign"
></i>
<span
class=
"sr"
>
${_('close')}
</span></a>
<a
href=
"#"
class=
"close-button"
><i
class=
"icon-remove-sign"
></i>
<span
class=
"sr"
>
${_('close')}
</span></a>
<div
class=
"modal-body"
>
<div
class=
"modal-body"
>
<h1
class=
"title"
>
${_("Upload New File")}
</h1>
<h1
class=
"title"
>
${_("Upload New File")}
</h1>
<h2>
${_("Max per-file size: {max_filesize}MB").format(max_filesize=max_file_size_in_mbs)}
</h2>
<p
class=
"file-name"
>
<p
class=
"file-name"
>
<div
class=
"progress-bar"
>
<div
class=
"progress-bar"
>
<div
class=
"progress-fill"
></div>
<div
class=
"progress-fill"
></div>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment