Commit 85e6c233 by Chris Dodge

work-in-flight for uploading/serving of static content for courses

parent 702c26ea
...@@ -2,14 +2,16 @@ from util.json_request import expect_json ...@@ -2,14 +2,16 @@ from util.json_request import expect_json
import json import json
import logging import logging
import sys import sys
import mimetypes
from collections import defaultdict 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.contrib.auth.decorators import login_required
from django.core.context_processors import csrf from django.core.context_processors import csrf
from django_future.csrf import ensure_csrf_cookie from django_future.csrf import ensure_csrf_cookie
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.conf import settings from django.conf import settings
from django import forms
from xmodule.modulestore import Location from xmodule.modulestore import Location
from xmodule.x_module import ModuleSystem from xmodule.x_module import ModuleSystem
...@@ -26,6 +28,13 @@ from functools import partial ...@@ -26,6 +28,13 @@ from functools import partial
from itertools import groupby from itertools import groupby
from operator import attrgetter 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__) log = logging.getLogger(__name__)
...@@ -89,9 +98,19 @@ def course_index(request, org, course, name): ...@@ -89,9 +98,19 @@ def course_index(request, org, course, name):
raise Http404 # TODO (vshnayder): better error raise Http404 # TODO (vshnayder): better error
# TODO (cpennington): These need to be read in from the active user # TODO (cpennington): These need to be read in from the active user
course = modulestore().get_item(location) _course = modulestore().get_item(location)
weeks = course.get_children() weeks = _course.get_children()
return render_to_response('course_index.html', {'weeks': weeks})
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 @login_required
...@@ -115,12 +134,13 @@ def edit_item(request): ...@@ -115,12 +134,13 @@ def edit_item(request):
lms_link = "{lms_base}/courses/{course_id}/jump_to/{location}".format( lms_link = "{lms_base}/courses/{course_id}/jump_to/{location}".format(
lms_base=settings.LMS_BASE, 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 # 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, location=item.location,
) )
else: else:
lms_link = None lms_link = None
return render_to_response('unit.html', { return render_to_response('unit.html', {
'contents': item.get_html(), 'contents': item.get_html(),
'js_module': item.js_module_name, 'js_module': item.js_module_name,
...@@ -390,3 +410,55 @@ def clone_item(request): ...@@ -390,3 +410,55 @@ def clone_item(request):
modulestore().update_children(parent_location, parent.definition.get('children', []) + [new_item.location.url()]) modulestore().update_children(parent_location, parent.definition.get('children', []) + [new_item.location.url()])
return HttpResponse() 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 = ( ...@@ -118,6 +118,7 @@ TEMPLATE_LOADERS = (
) )
MIDDLEWARE_CLASSES = ( MIDDLEWARE_CLASSES = (
'contentserver.middleware.StaticContentServer',
'django.middleware.cache.UpdateCacheMiddleware', 'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
...@@ -130,7 +131,7 @@ MIDDLEWARE_CLASSES = ( ...@@ -130,7 +131,7 @@ MIDDLEWARE_CLASSES = (
'track.middleware.TrackMiddleware', 'track.middleware.TrackMiddleware',
'mitxmako.middleware.MakoMiddleware', 'mitxmako.middleware.MakoMiddleware',
'django.middleware.transaction.TransactionMiddleware', 'django.middleware.transaction.TransactionMiddleware'
) )
############################ SIGNAL HANDLERS ################################ ############################ SIGNAL HANDLERS ################################
......
...@@ -28,6 +28,17 @@ MODULESTORE = { ...@@ -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 = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.sqlite3', 'ENGINE': 'django.db.backends.sqlite3',
......
...@@ -7,8 +7,11 @@ ...@@ -7,8 +7,11 @@
<%include file="widgets/navigation.html"/> <%include file="widgets/navigation.html"/>
<%include file="widgets/upload_assets.html"/>
<section class="main-content"> <section class="main-content">
</section> </section>
</section> </section>
</%block> </%block>
...@@ -17,7 +17,9 @@ urlpatterns = ('', ...@@ -17,7 +17,9 @@ urlpatterns = ('',
'contentstore.views.course_index', name='course_index'), 'contentstore.views.course_index', name='course_index'),
url(r'^github_service_hook$', 'github_sync.views.github_post_receive'), url(r'^github_service_hook$', 'github_sync.views.github_post_receive'),
url(r'^preview/modx/(?P<preview_id>[^/]*)/(?P<location>.*?)/(?P<dispatch>[^/]*)$', 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 # User creation and updating views
......
...@@ -10,6 +10,7 @@ Core methods ...@@ -10,6 +10,7 @@ Core methods
from django.core.cache import cache from django.core.cache import cache
from django.db import DEFAULT_DB_ALIAS from django.db import DEFAULT_DB_ALIAS
from xmodule.contentstore import StaticContent
from . import app_settings from . import app_settings
...@@ -107,3 +108,19 @@ def instance_key(model, instance_or_pk): ...@@ -107,3 +108,19 @@ def instance_key(model, instance_or_pk):
model._meta.module_name, model._meta.module_name,
getattr(instance_or_pk, 'pk', instance_or_pk), 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))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment