diff --git a/cms/djangoapps/auth/authz.py b/cms/djangoapps/auth/authz.py index 659588f6f6..460c9d6467 100644 --- a/cms/djangoapps/auth/authz.py +++ b/cms/djangoapps/auth/authz.py @@ -46,7 +46,7 @@ def create_all_course_groups(creator, location): def create_new_course_group(creator, location, role): groupname = get_course_groupname_for_role(location, role) - (group, created) =Group.get_or_create(name=groupname) + (group, created) =Group.objects.get_or_create(name=groupname) if created: group.save() diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py index 77dfe8c77b..8911150fbb 100644 --- a/cms/djangoapps/contentstore/views.py +++ b/cms/djangoapps/contentstore/views.py @@ -8,6 +8,8 @@ import os import StringIO import sys import time +import tarfile +import shutil from collections import defaultdict from uuid import uuid4 @@ -44,10 +46,11 @@ from xmodule.contentstore.content import StaticContent from cache_toolbox.core import set_cached_content, get_cached_content, del_cached_content 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 auth.authz import INSTRUCTOR_ROLE_NAME, STAFF_ROLE_NAME, create_all_course_groups 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 +from xmodule.modulestore.xml_importer import import_from_xml log = logging.getLogger(__name__) @@ -809,3 +812,46 @@ def asset_index(request, org, course, name): # points to the temporary edge page def edge(request): return render_to_response('university_profiles/edge.html', {}) + +def import_course(request): + if request.method != 'POST': + # (cdodge) @todo: Is there a way to do a - say - 'raise Http400'? + return HttpResponseBadRequest() + + filename = request.FILES['file'].name + + if not filename.endswith('.tar.gz'): + return HttpResponse(json.dumps({'ErrMsg': 'We only support uploading a .tar.gz file.'})) + + temp_filepath = settings.GITHUB_REPO_ROOT + '/' + filename + + logging.debug('importing course to {0}'.format(temp_filepath)) + + # stream out the uploaded files in chunks to disk + temp_file = open(temp_filepath, 'wb+') + for chunk in request.FILES['file'].chunks(): + temp_file.write(chunk) + temp_file.close() + + tf = tarfile.open(temp_filepath) + tf.extractall(settings.GITHUB_REPO_ROOT + '/') + + os.remove(temp_filepath) # remove the .tar.gz file + + # @todo: don't assume the top-level directory that was unziped was the same name (but without .tar.gz) + + course_dir = filename.replace('.tar.gz','') + + module_store, course_items = import_from_xml(modulestore('direct'), settings.GITHUB_REPO_ROOT, + [course_dir], load_error_modules=False,static_content_store=contentstore()) + + # remove content directory - we *shouldn't* need this any longer :-) + shutil.rmtree(temp_filepath.replace('.tar.gz', '')) + + logging.debug('new course at {0}'.format(course_items[0].location)) + + create_all_course_groups(request.user, course_items[0].location) + + + + return HttpResponse(json.dumps({'Status' : 'OK'})) diff --git a/cms/static/sass/base-style.scss b/cms/static/sass/base-style.scss index ec5ece2338..f2256f97ed 100644 --- a/cms/static/sass/base-style.scss +++ b/cms/static/sass/base-style.scss @@ -17,7 +17,6 @@ @import "static-pages"; @import "users"; @import "course-info"; -@import "edge"; @import "landing"; @import "graphics"; @import "modal"; diff --git a/cms/templates/index.html b/cms/templates/index.html index 36ddd1044b..e63bbbb84d 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -27,4 +27,7 @@ + +<%include file="widgets/import-course.html"/> + diff --git a/cms/templates/widgets/import-course.html b/cms/templates/widgets/import-course.html new file mode 100644 index 0000000000..d3af4951d1 --- /dev/null +++ b/cms/templates/widgets/import-course.html @@ -0,0 +1,45 @@ +<%! from django.core.urlresolvers import reverse %> + +
+
+ You can import an existing .tar.gz file of your course +
+ + +
+
+
+
0%
+
+ +
+
+ +
+ + + \ No newline at end of file diff --git a/cms/urls.py b/cms/urls.py index 47db1c024b..2fd4ed25e8 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -46,6 +46,8 @@ urlpatterns = ('', url(r'^edge$', 'contentstore.views.edge', name='edge'), url(r'^heartbeat$', include('heartbeat.urls')), + + url(r'import_course$', 'contentstore.views.import_course', name='import_course'), ) # User creation and updating views diff --git a/common/lib/xmodule/xmodule/modulestore/xml_importer.py b/common/lib/xmodule/xmodule/modulestore/xml_importer.py index 7adbd0aaa6..37e57bbcd5 100644 --- a/common/lib/xmodule/xmodule/modulestore/xml_importer.py +++ b/common/lib/xmodule/xmodule/modulestore/xml_importer.py @@ -40,9 +40,6 @@ def import_static_content(modules, data_dir, static_content_store): content_loc = StaticContent.compute_location(course_loc.org, course_loc.course, fullname_with_subpath) mime_type = mimetypes.guess_type(filename)[0] - print 'importing static asset {0} of mime-type {1} from path {2}'.format(content_loc, - mime_type, content_path) - f = open(content_path, 'rb') data = f.read() f.close() @@ -87,6 +84,7 @@ def import_from_xml(store, data_dir, course_dirs=None, # NOTE: the XmlModuleStore does not implement get_items() which would be a preferable means # to enumerate the entire collection of course modules. It will be left as a TBD to implement that # method on XmlModuleStore. + course_items = [] for course_id in module_store.modules.keys(): remap_dict = {} if static_content_store is not None: @@ -97,6 +95,7 @@ def import_from_xml(store, data_dir, course_dirs=None, if module.category == 'course': # HACK: for now we don't support progress tabs. There's a special metadata configuration setting for this. module.metadata['hide_progress_tab'] = True + course_items.append(module) if 'data' in module.definition: module_data = module.definition['data'] @@ -124,4 +123,4 @@ def import_from_xml(store, data_dir, course_dirs=None, store.update_metadata(module.location, dict(module.own_metadata)) - return module_store + return module_store, course_items