diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py
index bc33c7d1f6..fe1a64f5cf 100644
--- a/cms/djangoapps/contentstore/utils.py
+++ b/cms/djangoapps/contentstore/utils.py
@@ -74,3 +74,8 @@ def compute_unit_state(unit):
return UnitState.private
else:
return UnitState.public
+
+
+def get_date_display(date):
+ print date, type(date)
+ return date.strftime("%d %B, %Y at %I:%M %p")
diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py
index fa9bd30797..26ed3c53d2 100644
--- a/cms/djangoapps/contentstore/views.py
+++ b/cms/djangoapps/contentstore/views.py
@@ -1,3 +1,4 @@
+import traceback
from util.json_request import expect_json
import exceptions
import json
@@ -44,7 +45,7 @@ from cache_toolbox.core import set_cached_content, get_cached_content, del_cache
from auth.authz import is_user_in_course_group_role, get_users_in_course_group_by_role
from auth.authz import get_user_by_email, add_user_to_course_group, remove_user_from_course_group
from auth.authz import INSTRUCTOR_ROLE_NAME, STAFF_ROLE_NAME
-from .utils import get_course_location_for_item, get_lms_link_for_item, compute_unit_state
+from .utils import get_course_location_for_item, get_lms_link_for_item, compute_unit_state, get_date_display
from xmodule.templates import all_templates
@@ -618,7 +619,7 @@ def upload_asset(request, org, course, coursename):
if not has_access(request.user, location):
return HttpResponseForbidden()
- # Does the course actually exist?!?
+ # Does the course actually exist?!? Get anything from it to prove its existance
try:
item = modulestore().get_item(location)
@@ -635,23 +636,11 @@ def upload_asset(request, org, course, coursename):
mime_type = request.FILES['file'].content_type
filedata = request.FILES['file'].read()
- file_location = StaticContent.compute_location(org, course, name)
+ thumbnail_file_location = None
- content = StaticContent(file_location, name, mime_type, filedata)
-
- # first commit to the DB
- contentstore().save(content)
-
- # then remove the cache so we're not serving up stale content
- # NOTE: we're not re-populating the cache here as the DB owns the last-modified timestamp
- # which is used when serving up static content. This integrity is needed for
- # browser-side caching support. We *could* re-fetch the saved content so that we have the
- # timestamp populated, but we might as well wait for the first real request to come in
- # to re-populate the cache.
- del_cached_content(content.location)
-
- # if we're uploading an image, then let's generate a thumbnail so that we can
- # serve it up when needed without having to rescale on the fly
+ # if the upload asset is an image, we can generate a thumbnail from it
+ # let's do so now, so that we have the thumbnail location which we need
+ # so that the asset can point to it
if mime_type.split('/')[0] == 'image':
try:
# not sure if this is necessary, but let's rewind the stream just in case
@@ -673,24 +662,45 @@ def upload_asset(request, org, course, coursename):
thumbnail_file.seek(0)
# use a naming convention to associate originals with the thumbnail
- thumbnail_name = content.generate_thumbnail_name()
+ thumbnail_name = StaticContent.generate_thumbnail_name(name)
# then just store this thumbnail as any other piece of content
thumbnail_file_location = StaticContent.compute_location(org, course,
- thumbnail_name)
+ thumbnail_name, is_thumbnail=True)
thumbnail_content = StaticContent(thumbnail_file_location, thumbnail_name,
'image/jpeg', thumbnail_file)
contentstore().save(thumbnail_content)
-
+
# remove any cached content at this location, as thumbnails are treated just like any
# other bit of static content
del_cached_content(thumbnail_content.location)
+
+ # not sure if this is necessary, but let's rewind the stream just in case
+ request.FILES['file'].seek(0)
except:
# catch, log, and continue as thumbnails are not a hard requirement
logging.error('Failed to generate thumbnail for {0}. Continuing...'.format(name))
+ thumbnail_file_location = None
- return HttpResponse('Upload completed')
+ file_location = StaticContent.compute_location(org, course, name)
+
+ # create a StaticContent entity and point to the thumbnail
+ content = StaticContent(file_location, name, mime_type, filedata, thumbnail_location = thumbnail_file_location)
+
+ # first commit to the DB
+ contentstore().save(content)
+
+ # then remove the cache so we're not serving up stale content
+ # NOTE: we're not re-populating the cache here as the DB owns the last-modified timestamp
+ # which is used when serving up static content. This integrity is needed for
+ # browser-side caching support. We *could* re-fetch the saved content so that we have the
+ # timestamp populated, but we might as well wait for the first real request to come in
+ # to re-populate the cache.
+ del_cached_content(content.location)
+ response = HttpResponse('Upload completed')
+ response['asset_url'] = StaticContent.get_url_path_from_location(file_location)
+ return response
'''
This view will return all CMS users who are editors for the specified course
@@ -772,23 +782,69 @@ def remove_user(request, location):
return create_json_response()
-@login_required
-@ensure_csrf_cookie
-def asset_index(request, location):
- return render_to_response('asset_index.html',{})
# points to the temporary course landing page with log in and sign up
def landing(request, org, course, coursename):
return render_to_response('temp-course-landing.html', {})
+
def static_pages(request, org, course, coursename):
return render_to_response('static-pages.html', {})
+
def edit_static(request, org, course, coursename):
return render_to_response('edit-static-page.html', {})
+
def not_found(request):
return render_to_response('error.html', {'error': '404'})
+
def server_error(request):
- return render_to_response('error.html', {'error': '500'})
\ No newline at end of file
+ return render_to_response('error.html', {'error': '500'})
+
+
+@login_required
+@ensure_csrf_cookie
+def asset_index(request, org, course, name):
+ """
+ Display an editable asset library
+
+ org, course, name: Attributes of the Location for the item to edit
+ """
+ location = ['i4x', org, course, 'course', name]
+
+ # check that logged in user has permissions to this item
+ if not has_access(request.user, location):
+ raise PermissionDenied()
+
+ upload_asset_callback_url = reverse('upload_asset', kwargs = {
+ 'org' : org,
+ 'course' : course,
+ 'coursename' : name
+ })
+
+ course_reference = StaticContent.compute_location(org, course, name)
+ assets = contentstore().get_all_content_for_course(course_reference)
+ thumbnails = contentstore().get_all_content_thumbnails_for_course(course_reference)
+ asset_display = []
+ for asset in assets:
+ id = asset['_id']
+ display_info = {}
+ display_info['displayname'] = asset['displayname']
+ display_info['uploadDate'] = get_date_display(asset['uploadDate'])
+
+ asset_location = StaticContent.compute_location(id['org'], id['course'], id['name'])
+ display_info['url'] = StaticContent.get_url_path_from_location(asset_location)
+
+ # note, due to the schema change we may not have a 'thumbnail_location' in the result set
+ thumbnail_location = Location(asset.get('thumbnail_location', None))
+
+ display_info['thumb_url'] = StaticContent.get_url_path_from_location(thumbnail_location)
+
+ asset_display.append(display_info)
+
+ return render_to_response('asset_index.html', {
+ 'assets': asset_display,
+ 'upload_asset_callback_url': upload_asset_callback_url
+ })
diff --git a/cms/static/js/base.js b/cms/static/js/base.js
index 42afe992d5..c73eaead44 100644
--- a/cms/static/js/base.js
+++ b/cms/static/js/base.js
@@ -23,6 +23,10 @@ $(document).ready(function() {
$modalCover.bind('click', hideHistoryModal);
$('.assets .upload-button').bind('click', showUploadModal);
$('.upload-modal .close-button').bind('click', hideModal);
+
+ $('a.show-xml').toggle(showEmbeddableXML, hideEmbeddableXML);
+
+ $('a.copy-button').toggle(showEmbeddableXML, hideEmbeddableXML);
$('.unit .item-actions .delete-button').bind('click', deleteUnit);
$('.new-unit-item').bind('click', createNewUnit);
$('.save-subsection').bind('click', saveSubsection);
@@ -72,6 +76,17 @@ function removePolicyMetadata(e) {
_parent_el.remove();
else
_parent_el.appendTo("#policy-to-delete");
+
+function showEmbeddableXML(e) {
+ $ceiling = $(this).parents('tr');
+ if ($ceiling.length === 0) $ceiling = $(this).parents('.upload-modal');
+ $ceiling.find('.embeddable-xml').html('<img src="'+$(this).attr('href')+'"/>');
+}
+function hideEmbeddableXML(e) {
+ $ceiling = $(this).parents('tr');
+ console.log($ceiling.length)
+ if ($ceiling.length === 0) $ceiling = $(this).parents('.upload-modal');
+ $ceiling.find('.embeddable-xml').html("");
}
@@ -233,20 +248,40 @@ function showFileSelectionMenu(e) {
function startUpload(e) {
$('.upload-modal h1').html('Uploading…');
$('.upload-modal .file-name').html($('.file-input').val());
+ $('.upload-modal .file-chooser').ajaxSubmit({
+ beforeSend: resetUploadBar,
+ uploadProgress: showUploadFeedback,
+ complete: displayFinishedUpload
+ });
$('.upload-modal .choose-file-button').hide();
$('.upload-modal .progress-bar').removeClass('loaded').show();
- $('.upload-modal .progress-fill').html('').css('width', '0').animate({
- 'width': '100%'
- }, 1500);
- setTimeout(markAsLoaded, 1500);
+}
+
+function resetUploadBar(){
+ var percentVal = '0%';
+ $('.upload-modal .progress-fill').width(percentVal)
+ $('.upload-modal .progress-fill').html(percentVal);
+}
+
+function showUploadFeedback(event, position, total, percentComplete) {
+ var percentVal = percentComplete + '%';
+ $('.upload-modal .progress-fill').width(percentVal);
+ $('.upload-modal .progress-fill').html(percentVal);
+}
+
+function displayFinishedUpload(xhr) {
+ if(xhr.status = 200){
+ markAsLoaded();
+ }
+ $('.upload-modal .copy-button').attr('href', xhr.getResponseHeader('asset_url'));
+ $('.upload-modal .progress-fill').html(xhr.responseText);
+ $('.upload-modal .choose-file-button').html('Load Another File').show();
}
function markAsLoaded() {
$('.upload-modal .copy-button').css('display', 'inline-block');
$('.upload-modal .progress-bar').addClass('loaded');
- $('.upload-modal .progress-fill').html('loaded successfully');
- $('.upload-modal .choose-file-button').html('Load Another File').show();
-}
+}
function hideModal(e) {
e.preventDefault();
diff --git a/cms/static/sass/_assets.scss b/cms/static/sass/_assets.scss
index 82df497c9b..136b03280d 100644
--- a/cms/static/sass/_assets.scss
+++ b/cms/static/sass/_assets.scss
@@ -86,6 +86,9 @@
}
}
}
+ .show-xml {
+ @include blue-button;
+ }
}
.upload-modal {
diff --git a/cms/templates/asset_index.html b/cms/templates/asset_index.html
index 0069cae9ba..5940767c86 100644
--- a/cms/templates/asset_index.html
+++ b/cms/templates/asset_index.html
@@ -9,7 +9,8 @@
+
%block>
diff --git a/cms/templates/base.html b/cms/templates/base.html
index 915394f458..f847ad6f7b 100644
--- a/cms/templates/base.html
+++ b/cms/templates/base.html
@@ -33,6 +33,7 @@
+