work-in-flight for uploading/serving of static content for courses
This commit is contained in:
@@ -2,14 +2,16 @@ from util.json_request import expect_json
|
||||
import json
|
||||
import logging
|
||||
import sys
|
||||
import mimetypes
|
||||
from collections import defaultdict
|
||||
|
||||
from django.http import HttpResponse, Http404
|
||||
from django.http import HttpResponse, Http404, HttpResponseBadRequest, HttpResponseForbidden
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.context_processors import csrf
|
||||
from django_future.csrf import ensure_csrf_cookie
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from django import forms
|
||||
|
||||
from xmodule.modulestore import Location
|
||||
from xmodule.x_module import ModuleSystem
|
||||
@@ -26,6 +28,13 @@ from functools import partial
|
||||
from itertools import groupby
|
||||
from operator import attrgetter
|
||||
|
||||
from xmodule.contentstore.django import contentstore
|
||||
from xmodule.contentstore import StaticContent
|
||||
|
||||
#from django.core.cache import cache
|
||||
|
||||
from cache_toolbox.core import set_cached_content, get_cached_content
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -89,9 +98,19 @@ def course_index(request, org, course, name):
|
||||
raise Http404 # TODO (vshnayder): better error
|
||||
|
||||
# TODO (cpennington): These need to be read in from the active user
|
||||
course = modulestore().get_item(location)
|
||||
weeks = course.get_children()
|
||||
return render_to_response('course_index.html', {'weeks': weeks})
|
||||
_course = modulestore().get_item(location)
|
||||
weeks = _course.get_children()
|
||||
|
||||
upload_asset_callback_url = "/{org}/{course}/course/{name}/upload_asset".format(
|
||||
org = org,
|
||||
course = course,
|
||||
name = name
|
||||
)
|
||||
|
||||
return render_to_response('course_index.html', {
|
||||
'weeks': weeks,
|
||||
'upload_asset_callback_url': upload_asset_callback_url
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
@@ -115,12 +134,13 @@ def edit_item(request):
|
||||
lms_link = "{lms_base}/courses/{course_id}/jump_to/{location}".format(
|
||||
lms_base=settings.LMS_BASE,
|
||||
# TODO: These will need to be changed to point to the particular instance of this problem in the particular course
|
||||
course_id=modulestore().get_containing_courses(item.location)[0].id,
|
||||
course_id= modulestore().get_containing_courses(item.location)[0].id,
|
||||
location=item.location,
|
||||
)
|
||||
else:
|
||||
lms_link = None
|
||||
|
||||
|
||||
return render_to_response('unit.html', {
|
||||
'contents': item.get_html(),
|
||||
'js_module': item.js_module_name,
|
||||
@@ -390,3 +410,55 @@ def clone_item(request):
|
||||
modulestore().update_children(parent_location, parent.definition.get('children', []) + [new_item.location.url()])
|
||||
|
||||
return HttpResponse()
|
||||
|
||||
'''
|
||||
cdodge: this method allows for POST uploading of files into the course asset library, which will
|
||||
be supported by GridFS in MongoDB.
|
||||
'''
|
||||
#@login_required
|
||||
#@ensure_csrf_cookie
|
||||
def upload_asset(request, org, course, coursename):
|
||||
|
||||
if request.method != 'POST':
|
||||
# (cdodge) @todo: Is there a way to do a - say - 'raise Http400'?
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
# construct a location from the passed in path
|
||||
location = ['i4x', org, course, 'course', coursename]
|
||||
if not has_access(request.user, location):
|
||||
return HttpResponseForbidden()
|
||||
|
||||
# Does the course actually exist?!?
|
||||
|
||||
try:
|
||||
item = modulestore().get_item(location)
|
||||
except:
|
||||
# no return it as a Bad Request response
|
||||
logging.error('Could not find course' + location)
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
# compute a 'filename' which is similar to the location formatting, we're using the 'filename'
|
||||
# nomenclature since we're using a FileSystem paradigm here. We're just imposing
|
||||
# the Location string formatting expectations to keep things a bit more consistent
|
||||
|
||||
name = request.FILES['file'].name
|
||||
mime_type = request.FILES['file'].content_type
|
||||
filedata = request.FILES['file'].read()
|
||||
|
||||
file_location = StaticContent.compute_location_filename(org, course, name)
|
||||
|
||||
content = StaticContent(file_location, name, mime_type, filedata)
|
||||
|
||||
# first commit to the DB
|
||||
contentstore().update(content)
|
||||
|
||||
# then update the cache so we're not serving up stale content
|
||||
set_cached_content(content)
|
||||
|
||||
return HttpResponse('Upload completed')
|
||||
|
||||
|
||||
|
||||
class UploadFileForm(forms.Form):
|
||||
title = forms.CharField(max_length=50)
|
||||
file = forms.FileField()
|
||||
|
||||
@@ -118,6 +118,7 @@ TEMPLATE_LOADERS = (
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'contentserver.middleware.StaticContentServer',
|
||||
'django.middleware.cache.UpdateCacheMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
@@ -130,7 +131,7 @@ MIDDLEWARE_CLASSES = (
|
||||
'track.middleware.TrackMiddleware',
|
||||
'mitxmako.middleware.MakoMiddleware',
|
||||
|
||||
'django.middleware.transaction.TransactionMiddleware',
|
||||
'django.middleware.transaction.TransactionMiddleware'
|
||||
)
|
||||
|
||||
############################ SIGNAL HANDLERS ################################
|
||||
|
||||
@@ -28,6 +28,17 @@ MODULESTORE = {
|
||||
}
|
||||
}
|
||||
|
||||
# cdodge: This is the specifier for the MongoDB (using GridFS) backed static content store
|
||||
# This is for static content for courseware, not system static content (e.g. javascript, css, edX branding, etc)
|
||||
CONTENTSTORE = {
|
||||
'ENGINE': 'xmodule.contentstore.mongo.MongoContentStore',
|
||||
'OPTIONS': {
|
||||
'host': 'localhost',
|
||||
'db' : 'xcontent',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
|
||||
@@ -7,8 +7,11 @@
|
||||
|
||||
<%include file="widgets/navigation.html"/>
|
||||
|
||||
<%include file="widgets/upload_assets.html"/>
|
||||
|
||||
<section class="main-content">
|
||||
</section>
|
||||
|
||||
|
||||
</section>
|
||||
</%block>
|
||||
|
||||
@@ -17,7 +17,9 @@ urlpatterns = ('',
|
||||
'contentstore.views.course_index', name='course_index'),
|
||||
url(r'^github_service_hook$', 'github_sync.views.github_post_receive'),
|
||||
url(r'^preview/modx/(?P<preview_id>[^/]*)/(?P<location>.*?)/(?P<dispatch>[^/]*)$',
|
||||
'contentstore.views.preview_dispatch', name='preview_dispatch')
|
||||
'contentstore.views.preview_dispatch', name='preview_dispatch'),
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)/upload_asset$',
|
||||
'contentstore.views.upload_asset', name='upload_asset')
|
||||
)
|
||||
|
||||
# User creation and updating views
|
||||
|
||||
@@ -10,6 +10,7 @@ Core methods
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.db import DEFAULT_DB_ALIAS
|
||||
from xmodule.contentstore import StaticContent
|
||||
|
||||
from . import app_settings
|
||||
|
||||
@@ -107,3 +108,19 @@ def instance_key(model, instance_or_pk):
|
||||
model._meta.module_name,
|
||||
getattr(instance_or_pk, 'pk', instance_or_pk),
|
||||
)
|
||||
|
||||
def content_key(filename):
|
||||
return 'content:%s' % (filename)
|
||||
|
||||
def set_cached_content(content):
|
||||
cache.set(content_key(content.filename), content)
|
||||
|
||||
def get_cached_content(filename):
|
||||
return cache.get(content_key(filename))
|
||||
|
||||
|
||||
#def set_cached_content(filename, content_type, data):
|
||||
# cache.set(content_key(filename), (filename, content_type, data))
|
||||
|
||||
#def get_cached_content(filename):
|
||||
# return cache.get(content_key(filename))
|
||||
|
||||
Reference in New Issue
Block a user