diff --git a/cms/templates/widgets/upload_assets.html b/cms/templates/widgets/upload_assets.html new file mode 100644 index 0000000000..876166fdf7 --- /dev/null +++ b/cms/templates/widgets/upload_assets.html @@ -0,0 +1,44 @@ +
+
+ You can upload file assets to reference in your courseware +
+ + +
+
+
+
+
0%
+
+ +
+ +
+ + + diff --git a/common/djangoapps/contentserver/__init__.py b/common/djangoapps/contentserver/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/djangoapps/contentserver/middleware.py b/common/djangoapps/contentserver/middleware.py new file mode 100644 index 0000000000..48b760a8d3 --- /dev/null +++ b/common/djangoapps/contentserver/middleware.py @@ -0,0 +1,36 @@ +import logging + +from django.http import HttpResponse, Http404 + +from xmodule.contentstore.django import contentstore +from xmodule.contentstore import StaticContent +from cache_toolbox.core import get_cached_content, set_cached_content +from xmodule.exceptions import NotFoundError + + +class StaticContentServer(object): + def __init__(self): + self.match_tag = StaticContent.get_location_tag() + + def process_request(self, request): + # look to see if the request is prefixed with 'c4x' tag + if request.path.startswith('/' + self.match_tag): + + # first look in our cache so we don't have to round-trip to the DB + content = get_cached_content(request.path) + if content is None: + # nope, not in cache, let's fetch from DB + try: + content = contentstore().find(request.path) + except NotFoundError: + raise Http404 + + # since we fetched it from DB, let's cache it going forward + set_cached_content(content) + else: + logging.debug('cache hit on {0}'.format(content.filename)) + + response = HttpResponse(content.data, content_type=content.content_type) + response['Content-Disposition'] = 'attachment; filename={0}'.format(content.name) + + return response diff --git a/common/lib/xmodule/xmodule/contentstore/__init__.py b/common/lib/xmodule/xmodule/contentstore/__init__.py new file mode 100644 index 0000000000..08658ea721 --- /dev/null +++ b/common/lib/xmodule/xmodule/contentstore/__init__.py @@ -0,0 +1,15 @@ +class StaticContent(object): + def __init__(self, filename, name, content_type, data): + self.filename = filename + self.name = name + self.content_type = content_type + self.data = data + + @staticmethod + def get_location_tag(): + return 'c4x' + + @staticmethod + def compute_location_filename(org, course, name): + return '/{0}/{1}/{2}/asset/{3}'.format(StaticContent.get_location_tag(), org, course, name) + diff --git a/common/lib/xmodule/xmodule/contentstore/django.py b/common/lib/xmodule/xmodule/contentstore/django.py new file mode 100644 index 0000000000..d8b3084135 --- /dev/null +++ b/common/lib/xmodule/xmodule/contentstore/django.py @@ -0,0 +1,29 @@ +from __future__ import absolute_import +from importlib import import_module +from os import environ + +from django.conf import settings + +_CONTENTSTORE = None + +def load_function(path): + """ + Load a function by name. + + path is a string of the form "path.to.module.function" + returns the imported python object `function` from `path.to.module` + """ + module_path, _, name = path.rpartition('.') + return getattr(import_module(module_path), name) + + +def contentstore(): + global _CONTENTSTORE + + if _CONTENTSTORE is None: + class_ = load_function(settings.CONTENTSTORE['ENGINE']) + options = {} + options.update(settings.CONTENTSTORE['OPTIONS']) + _CONTENTSTORE = class_(**options) + + return _CONTENTSTORE diff --git a/common/lib/xmodule/xmodule/contentstore/mongo.py b/common/lib/xmodule/xmodule/contentstore/mongo.py new file mode 100644 index 0000000000..eddb162539 --- /dev/null +++ b/common/lib/xmodule/xmodule/contentstore/mongo.py @@ -0,0 +1,32 @@ +from pymongo import Connection +import gridfs +from gridfs.errors import NoFile + +import sys +import logging + +from . import StaticContent +from xmodule.exceptions import NotFoundError + + +class MongoContentStore(object): + def __init__(self, host, db, port=27017): + logging.debug( 'Using MongoDB for static content serving at host={0} db={1}'.format(host,db)) + _db = Connection(host=host, port=port)[db] + self.fs = gridfs.GridFS(_db) + + def update(self, content): + with self.fs.new_file(filename=content.filename, content_type=content.content_type, displayname=content.name) as fp: + fp.write(content.data) + return content + + def find(self, filename): + try: + with self.fs.get_last_version(filename) as fp: + logging.debug('fetched {0}'.format(fp.name)) + return StaticContent(fp.filename, fp.displayname, fp.content_type, fp.read()) + except NoFile: + raise NotFoundError() + + +