Merge pull request #848 from MITx/feature/cas/lyla/asset-store-integration
Feature/cas/lyla/asset store integration
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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'})
|
||||
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
|
||||
})
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -86,6 +86,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.show-xml {
|
||||
@include blue-button;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-modal {
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
<div class="inner-wrapper">
|
||||
<h1>Asset Library</h1>
|
||||
<div class="page-actions">
|
||||
<a href="#" class="upload-button">Upload New File</a><input type="text" class="asset-search-input search wip-box" placeholder="search assets" />
|
||||
<a href="#" class="upload-button">Upload New File</a>
|
||||
<input type="text" class="asset-search-input search wip-box" placeholder="search assets" />
|
||||
</div>
|
||||
<article class="asset-library">
|
||||
<table>
|
||||
@@ -22,149 +23,26 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
% for asset in assets:
|
||||
<tr>
|
||||
<td class="thumb-col">
|
||||
<div class="thumb"><img src="http://dribbble.s3.amazonaws.com/users/3185/screenshots/149571/picture_39.png"></div>
|
||||
<div class="thumb"><img src="${asset['thumb_url']}"></div>
|
||||
</td>
|
||||
<td class="name-col">
|
||||
<a href="#" class="filename">raygun-1.jpg</a>
|
||||
<a href="${asset['url']}" class="filename">${asset['displayname']}</a>
|
||||
<div class="embeddable-xml"></div>
|
||||
</td>
|
||||
<td class="date-col">
|
||||
10/2/2012
|
||||
${asset['uploadDate']}
|
||||
</td>
|
||||
<td class="embed-col">
|
||||
<a href="#">copy</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="thumb-col">
|
||||
<div class="thumb"><img src="http://dribbble.s3.amazonaws.com/users/4573/screenshots/157708/final.png"></div>
|
||||
</td>
|
||||
<td class="name-col">
|
||||
<a href="#" class="filename">raygun-2.jpg</a>
|
||||
</td>
|
||||
<td class="date-col">
|
||||
10/2/2012
|
||||
</td>
|
||||
<td class="embed-col">
|
||||
<a href="#">copy</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="thumb-col">
|
||||
<div class="thumb"><img src="http://dribbble.s3.amazonaws.com/users/15101/screenshots/228625/star_wars_lightsbaer_2.jpg"></div>
|
||||
</td>
|
||||
<td class="name-col">
|
||||
<a href="#" class="filename">raygun-2.jpg</a>
|
||||
</td>
|
||||
<td class="date-col">
|
||||
10/2/2012
|
||||
</td>
|
||||
<td class="embed-col">
|
||||
<a href="#">copy</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="thumb-col">
|
||||
<div class="thumb"><img src="http://dribbble.s3.amazonaws.com/users/3185/screenshots/149571/picture_39.png"></div>
|
||||
</td>
|
||||
<td class="name-col">
|
||||
<a href="#" class="filename">raygun-1.jpg</a>
|
||||
</td>
|
||||
<td class="date-col">
|
||||
10/2/2012
|
||||
</td>
|
||||
<td class="embed-col">
|
||||
<a href="#">copy</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="thumb-col">
|
||||
<div class="thumb"><img src="http://dribbble.s3.amazonaws.com/users/4573/screenshots/157708/final.png"></div>
|
||||
</td>
|
||||
<td class="name-col">
|
||||
<a href="#" class="filename">raygun-2.jpg</a>
|
||||
</td>
|
||||
<td class="date-col">
|
||||
10/2/2012
|
||||
</td>
|
||||
<td class="embed-col">
|
||||
<a href="#">copy</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="thumb-col">
|
||||
<div class="thumb"><img src="http://dribbble.s3.amazonaws.com/users/15101/screenshots/228625/star_wars_lightsbaer_2.jpg"></div>
|
||||
</td>
|
||||
<td class="name-col">
|
||||
<a href="#" class="filename">raygun-2.jpg</a>
|
||||
</td>
|
||||
<td class="date-col">
|
||||
10/2/2012
|
||||
</td>
|
||||
<td class="embed-col">
|
||||
<a href="#">copy</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="thumb-col">
|
||||
<div class="thumb"><img src="http://dribbble.s3.amazonaws.com/users/3185/screenshots/149571/picture_39.png"></div>
|
||||
</td>
|
||||
<td class="name-col">
|
||||
<a href="#" class="filename">raygun-1.jpg</a>
|
||||
</td>
|
||||
<td class="date-col">
|
||||
10/2/2012
|
||||
</td>
|
||||
<td class="embed-col">
|
||||
<a href="#">copy</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="thumb-col">
|
||||
<div class="thumb"><img src="http://dribbble.s3.amazonaws.com/users/4573/screenshots/157708/final.png"></div>
|
||||
</td>
|
||||
<td class="name-col">
|
||||
<a href="#" class="filename">raygun-2.jpg</a>
|
||||
</td>
|
||||
<td class="date-col">
|
||||
10/2/2012
|
||||
</td>
|
||||
<td class="embed-col">
|
||||
<a href="#">copy</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="thumb-col">
|
||||
<div class="thumb"><img src="http://dribbble.s3.amazonaws.com/users/15101/screenshots/228625/star_wars_lightsbaer_2.jpg"></div>
|
||||
</td>
|
||||
<td class="name-col">
|
||||
<a href="#" class="filename">raygun-2.jpg</a>
|
||||
</td>
|
||||
<td class="date-col">
|
||||
10/2/2012
|
||||
</td>
|
||||
<td class="embed-col">
|
||||
<a href="#">copy</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="thumb-col">
|
||||
<div class="thumb"><img src="http://dribbble.s3.amazonaws.com/users/3185/screenshots/149571/picture_39.png"></div>
|
||||
</td>
|
||||
<td class="name-col">
|
||||
<a href="#" class="filename">raygun-1.jpg</a>
|
||||
</td>
|
||||
<td class="date-col">
|
||||
10/2/2012
|
||||
</td>
|
||||
<td class="embed-col">
|
||||
<a href="#">copy</a>
|
||||
<a class="show-xml" href="${asset['url']}">XML</a>
|
||||
</td>
|
||||
</tr>
|
||||
% endfor
|
||||
</tbody>
|
||||
</table>
|
||||
<nav class="pagination">
|
||||
<nav class="pagination wip-box">
|
||||
Page:
|
||||
<ol class="pages">
|
||||
<li>1</li>
|
||||
@@ -187,14 +65,17 @@
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill"></div>
|
||||
</div>
|
||||
<a href="#" class="copy-button">Copy Embed Link</a>
|
||||
<div class="file-chooser">
|
||||
<div class="embeddable-xml"></div>
|
||||
<a href="#" class="copy-button">Show Embeddable XML</a>
|
||||
<form class="file-chooser" action="${upload_asset_callback_url}"
|
||||
method="post" enctype="multipart/form-data">
|
||||
<a href="#" class="choose-file-button">Choose File</a>
|
||||
<input type="file" class="file-input">
|
||||
</div>
|
||||
<input type="file" class="file-input" name="file">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-cover"></div>
|
||||
|
||||
|
||||
</%block>
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
<script src="${static.url('js/vendor/jquery.cookie.js')}"></script>
|
||||
<script src="${static.url('js/vendor/jquery.leanModal.min.js')}"></script>
|
||||
<script src="${static.url('js/vendor/jquery.tablednd.js')}"></script>
|
||||
<script src="http://malsup.github.com/jquery.form.js"></script>
|
||||
<script type="text/javascript">
|
||||
document.write('\x3Cscript type="text/javascript" src="' +
|
||||
document.location.protocol + '//www.youtube.com/player_api">\x3C/script>');
|
||||
|
||||
@@ -30,16 +30,17 @@ urlpatterns = ('',
|
||||
'contentstore.views.remove_user', name='remove_user'),
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<name>[^/]+)/remove_user$',
|
||||
'contentstore.views.remove_user', name='remove_user'),
|
||||
url(r'^assets/(?P<location>.*?)$', 'contentstore.views.asset_index', name='asset_index'),
|
||||
url(r'^pages/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$', 'contentstore.views.static_pages', name='static_pages'),
|
||||
url(r'^edit_static/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$', 'contentstore.views.edit_static', name='edit_static'),
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/assets/(?P<name>[^/]+)$', 'contentstore.views.asset_index', name='asset_index'),
|
||||
|
||||
# temporary landing page for a course
|
||||
url(r'^landing/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$', 'contentstore.views.landing', name='landing'),
|
||||
|
||||
url(r'^not_found$', 'contentstore.views.not_found', name='not_found'),
|
||||
url(r'^server_error$', 'contentstore.views.server_error', name='server_error')
|
||||
url(r'^server_error$', 'contentstore.views.server_error', name='server_error'),
|
||||
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/assets/(?P<name>[^/]+)$', 'contentstore.views.asset_index', name='asset_index'),
|
||||
)
|
||||
|
||||
# User creation and updating views
|
||||
|
||||
@@ -1,30 +1,32 @@
|
||||
XASSET_LOCATION_TAG = 'c4x'
|
||||
XASSET_SRCREF_PREFIX = 'xasset:'
|
||||
|
||||
XASSET_THUMBNAIL_TAIL_NAME = '.thumbnail.jpg'
|
||||
XASSET_THUMBNAIL_TAIL_NAME = '.jpg'
|
||||
|
||||
import os
|
||||
import logging
|
||||
from xmodule.modulestore import Location
|
||||
|
||||
class StaticContent(object):
|
||||
def __init__(self, loc, name, content_type, data, last_modified_at=None):
|
||||
def __init__(self, loc, name, content_type, data, last_modified_at=None, thumbnail_location=None):
|
||||
self.location = loc
|
||||
self.name = name #a display string which can be edited, and thus not part of the location which needs to be fixed
|
||||
self.content_type = content_type
|
||||
self.data = data
|
||||
self.last_modified_at = last_modified_at
|
||||
self.thumbnail_location = thumbnail_location
|
||||
|
||||
@property
|
||||
def is_thumbnail(self):
|
||||
return self.name.endswith(XASSET_THUMBNAIL_TAIL_NAME)
|
||||
|
||||
def generate_thumbnail_name(self):
|
||||
return ('{0}'+XASSET_THUMBNAIL_TAIL_NAME).format(os.path.splitext(self.name)[0])
|
||||
return self.location.category == 'thumbnail'
|
||||
|
||||
@staticmethod
|
||||
def compute_location(org, course, name, revision=None):
|
||||
return Location([XASSET_LOCATION_TAG, org, course, 'asset', name, revision])
|
||||
def generate_thumbnail_name(original_name):
|
||||
return ('{0}'+XASSET_THUMBNAIL_TAIL_NAME).format(os.path.splitext(original_name)[0])
|
||||
|
||||
@staticmethod
|
||||
def compute_location(org, course, name, revision=None, is_thumbnail=False):
|
||||
return Location([XASSET_LOCATION_TAG, org, course, 'asset' if not is_thumbnail else 'thumbnail', Location.clean(name), revision])
|
||||
|
||||
def get_id(self):
|
||||
return StaticContent.get_id_from_location(self.location)
|
||||
@@ -34,7 +36,10 @@ class StaticContent(object):
|
||||
|
||||
@staticmethod
|
||||
def get_url_path_from_location(location):
|
||||
return "/{tag}/{org}/{course}/{category}/{name}".format(**location.dict())
|
||||
if location is not None:
|
||||
return "/{tag}/{org}/{course}/{category}/{name}".format(**location.dict())
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_id_from_location(location):
|
||||
@@ -65,4 +70,23 @@ class ContentStore(object):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_all_content_for_course(self, location):
|
||||
'''
|
||||
Returns a list of all static assets for a course. The return format is a list of dictionary elements. Example:
|
||||
|
||||
[
|
||||
|
||||
{u'displayname': u'profile.jpg', u'chunkSize': 262144, u'length': 85374,
|
||||
u'uploadDate': datetime.datetime(2012, 10, 3, 5, 41, 54, 183000), u'contentType': u'image/jpeg',
|
||||
u'_id': {u'category': u'asset', u'name': u'profile.jpg', u'course': u'6.002x', u'tag': u'c4x',
|
||||
u'org': u'MITx', u'revision': None}, u'md5': u'36dc53519d4b735eb6beba51cd686a0e'},
|
||||
|
||||
{u'displayname': u'profile.thumbnail.jpg', u'chunkSize': 262144, u'length': 4073,
|
||||
u'uploadDate': datetime.datetime(2012, 10, 3, 5, 41, 54, 196000), u'contentType': u'image/jpeg',
|
||||
u'_id': {u'category': u'asset', u'name': u'profile.thumbnail.jpg', u'course': u'6.002x', u'tag': u'c4x',
|
||||
u'org': u'MITx', u'revision': None}, u'md5': u'ff1532598830e3feac91c2449eaa60d6'},
|
||||
|
||||
....
|
||||
|
||||
]
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -28,7 +28,9 @@ class MongoContentStore(ContentStore):
|
||||
if self.fs.exists({"_id" : id}):
|
||||
self.fs.delete(id)
|
||||
|
||||
with self.fs.new_file(_id = id, filename=content.get_url_path(), content_type=content.content_type, displayname=content.name) as fp:
|
||||
with self.fs.new_file(_id = id, filename=content.get_url_path(), content_type=content.content_type,
|
||||
displayname=content.name, thumbnail_location=content.thumbnail_location) as fp:
|
||||
|
||||
fp.write(content.data)
|
||||
|
||||
return content
|
||||
@@ -38,11 +40,18 @@ class MongoContentStore(ContentStore):
|
||||
id = StaticContent.get_id_from_location(location)
|
||||
try:
|
||||
with self.fs.get(id) as fp:
|
||||
return StaticContent(location, fp.displayname, fp.content_type, fp.read(), fp.uploadDate)
|
||||
return StaticContent(location, fp.displayname, fp.content_type, fp.read(),
|
||||
fp.uploadDate, thumbnail_location = fp.thumbnail_location if 'thumbnail_location' in fp else None)
|
||||
except NoFile:
|
||||
raise NotFoundError()
|
||||
|
||||
def get_all_content_info_for_course(self, location):
|
||||
def get_all_content_thumbnails_for_course(self, location):
|
||||
return self._get_all_content_for_course(location, get_thumbnails = True)
|
||||
|
||||
def get_all_content_for_course(self, location):
|
||||
return self._get_all_content_for_course(location, get_thumbnails = False)
|
||||
|
||||
def _get_all_content_for_course(self, location, get_thumbnails = False):
|
||||
'''
|
||||
Returns a list of all static assets for a course. The return format is a list of dictionary elements. Example:
|
||||
|
||||
@@ -62,7 +71,8 @@ class MongoContentStore(ContentStore):
|
||||
|
||||
]
|
||||
'''
|
||||
course_filter = Location(XASSET_LOCATION_TAG, category="asset",course=location.course,org=location.org)
|
||||
course_filter = Location(XASSET_LOCATION_TAG, category="asset" if not get_thumbnails else "thumbnail",
|
||||
course=location.course,org=location.org)
|
||||
# 'borrow' the function 'location_to_query' from the Mongo modulestore implementation
|
||||
items = self.fs_files.find(location_to_query(course_filter))
|
||||
return list(items)
|
||||
|
||||
Reference in New Issue
Block a user