diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py
index da6611f248..6692192590 100644
--- a/cms/djangoapps/contentstore/views.py
+++ b/cms/djangoapps/contentstore/views.py
@@ -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()
diff --git a/cms/envs/common.py b/cms/envs/common.py
index dc82af85af..7190ba9e51 100644
--- a/cms/envs/common.py
+++ b/cms/envs/common.py
@@ -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 ################################
diff --git a/cms/envs/dev.py b/cms/envs/dev.py
index fc2a5a5684..dd0e0337f6 100644
--- a/cms/envs/dev.py
+++ b/cms/envs/dev.py
@@ -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',
diff --git a/cms/templates/course_index.html b/cms/templates/course_index.html
index e490ad7817..347e93ef4a 100644
--- a/cms/templates/course_index.html
+++ b/cms/templates/course_index.html
@@ -7,8 +7,11 @@
<%include file="widgets/navigation.html"/>
+ <%include file="widgets/upload_assets.html"/>
+
+
%block>
diff --git a/cms/urls.py b/cms/urls.py
index e51ae59b08..ddd54adc65 100644
--- a/cms/urls.py
+++ b/cms/urls.py
@@ -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[^/]*)/(?P.*?)/(?P[^/]*)$',
- 'contentstore.views.preview_dispatch', name='preview_dispatch')
+ 'contentstore.views.preview_dispatch', name='preview_dispatch'),
+ url(r'^(?P[^/]+)/(?P[^/]+)/course/(?P[^/]+)/upload_asset$',
+ 'contentstore.views.upload_asset', name='upload_asset')
)
# User creation and updating views
diff --git a/common/djangoapps/cache_toolbox/core.py b/common/djangoapps/cache_toolbox/core.py
index 208be34a73..85e1c8a246 100644
--- a/common/djangoapps/cache_toolbox/core.py
+++ b/common/djangoapps/cache_toolbox/core.py
@@ -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))