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"/>
+
%block>
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